Repository: AppsFlyerSDK/appsflyer-unity-plugin Branch: master Commit: 087f87fc8179 Files: 212 Total size: 686.6 KB Directory structure: gitextract_u2enc1o6/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── config.yml │ └── workflows/ │ ├── Scripts/ │ │ ├── checkJira.sh │ │ └── extractPackage.py │ ├── activation.yml │ ├── check_packages.yml │ ├── close_inactive_issues.yml │ ├── issues_workflow.yml │ ├── main.yml │ ├── readme_sync.yml │ ├── release_QA_workflow.yml │ ├── release_production_workflow.yml │ ├── release_to_QA.yml │ ├── release_to_production.yml │ ├── responseToSupportIssue.yml │ ├── responseToSupportIssueOnOpen.yml │ ├── test-unity-auth.yml │ └── update_links.yml ├── .gitignore ├── Assets/ │ ├── AppsFlyer/ │ │ ├── AFAdRevenueData.cs │ │ ├── AFAdRevenueData.cs.meta │ │ ├── AFInAppEvents.cs │ │ ├── AFInAppEvents.cs.meta │ │ ├── AFMiniJSON.cs │ │ ├── AFMiniJSON.cs.meta │ │ ├── AFPurchaseDetailsAndroid.cs │ │ ├── AFPurchaseDetailsAndroid.cs.meta │ │ ├── AFSDKPurchaseDetailsIOS.cs │ │ ├── AFSDKPurchaseDetailsIOS.cs.meta │ │ ├── AFSDKValidateAndLogResult.cs │ │ ├── AFSDKValidateAndLogResult.cs.meta │ │ ├── AppsFlyer.asmdef │ │ ├── AppsFlyer.asmdef.meta │ │ ├── AppsFlyer.cs │ │ ├── AppsFlyer.cs.meta │ │ ├── AppsFlyerAndroid.cs │ │ ├── AppsFlyerAndroid.cs.meta │ │ ├── AppsFlyerConsent.cs │ │ ├── AppsFlyerConsent.cs.meta │ │ ├── AppsFlyerEventArgs.cs │ │ ├── AppsFlyerEventArgs.cs.meta │ │ ├── AppsFlyerObject.prefab │ │ ├── AppsFlyerObject.prefab.meta │ │ ├── AppsFlyerObjectScript.cs │ │ ├── AppsFlyerObjectScript.cs.meta │ │ ├── AppsFlyerPurchaseConnector.cs │ │ ├── AppsFlyerPurchaseConnector.cs.meta │ │ ├── AppsFlyeriOS.cs │ │ ├── AppsFlyeriOS.cs.meta │ │ ├── Editor/ │ │ │ ├── AppsFlyer.Editor.asmdef │ │ │ ├── AppsFlyer.Editor.asmdef.meta │ │ │ ├── AppsFlyerDependencies.xml │ │ │ ├── AppsFlyerDependencies.xml.meta │ │ │ ├── AppsFlyerObjectEditor.cs │ │ │ ├── AppsFlyerObjectEditor.cs.meta │ │ │ └── appsflyer_logo.png.meta │ │ ├── Editor.meta │ │ ├── IAppsFlyerAndroidBridge.cs │ │ ├── IAppsFlyerAndroidBridge.cs.meta │ │ ├── IAppsFlyerConversionData.cs │ │ ├── IAppsFlyerConversionData.cs.meta │ │ ├── IAppsFlyerIOSBridge.cs │ │ ├── IAppsFlyerIOSBridge.cs.meta │ │ ├── IAppsFlyerNativeBridge.cs │ │ ├── IAppsFlyerNativeBridge.cs.meta │ │ ├── IAppsFlyerPurchaseValidation.cs │ │ ├── IAppsFlyerPurchaseValidation.cs.meta │ │ ├── IAppsFlyerUserInvite.cs │ │ ├── IAppsFlyerUserInvite.cs.meta │ │ ├── IAppsFlyerValidateAndLog.cs │ │ ├── IAppsFlyerValidateAndLog.cs.meta │ │ ├── IAppsFlyerValidateReceipt.cs │ │ ├── IAppsFlyerValidateReceipt.cs.meta │ │ ├── Mac/ │ │ │ ├── AppsFlyerBundle.bundle/ │ │ │ │ ├── Contents/ │ │ │ │ │ ├── Info.plist │ │ │ │ │ ├── Info.plist.meta │ │ │ │ │ ├── MacOS/ │ │ │ │ │ │ ├── AppsFlyerBundle │ │ │ │ │ │ └── AppsFlyerBundle.meta │ │ │ │ │ ├── MacOS.meta │ │ │ │ │ ├── _CodeSignature/ │ │ │ │ │ │ ├── CodeResources │ │ │ │ │ │ └── CodeResources.meta │ │ │ │ │ └── _CodeSignature.meta │ │ │ │ └── Contents.meta │ │ │ └── AppsFlyerBundle.bundle.meta │ │ ├── Mac.meta │ │ ├── Plugins/ │ │ │ ├── iOS/ │ │ │ │ ├── AFUnityStoreKit2Bridge.swift │ │ │ │ ├── AFUnityStoreKit2Bridge.swift.meta │ │ │ │ ├── AFUnityUtils.h │ │ │ │ ├── AFUnityUtils.h.meta │ │ │ │ ├── AFUnityUtils.mm │ │ │ │ ├── AFUnityUtils.mm.meta │ │ │ │ ├── AppsFlyer+AppController.m │ │ │ │ ├── AppsFlyer+AppController.m.meta │ │ │ │ ├── AppsFlyerAppController.mm │ │ │ │ ├── AppsFlyerAppController.mm.meta │ │ │ │ ├── AppsFlyerAttribution.h │ │ │ │ ├── AppsFlyerAttribution.h.meta │ │ │ │ ├── AppsFlyerAttribution.m │ │ │ │ ├── AppsFlyerAttribution.m.meta │ │ │ │ ├── AppsFlyeriOSWrapper.h │ │ │ │ ├── AppsFlyeriOSWrapper.h.meta │ │ │ │ ├── AppsFlyeriOSWrapper.mm │ │ │ │ └── AppsFlyeriOSWrapper.mm.meta │ │ │ └── iOS.meta │ │ ├── Plugins.meta │ │ ├── ProductPurchase.cs │ │ ├── ProductPurchase.cs.meta │ │ ├── Tests/ │ │ │ ├── Castle.Core.dll.meta │ │ │ ├── NSubstitute.dll.meta │ │ │ ├── System.Runtime.CompilerServices.Unsafe.dll.meta │ │ │ ├── System.Threading.Tasks.Extensions.dll.meta │ │ │ ├── Tests.asmdef │ │ │ ├── Tests.asmdef.meta │ │ │ ├── Tests_Suite.cs │ │ │ └── Tests_Suite.cs.meta │ │ ├── Tests.meta │ │ ├── Windows/ │ │ │ ├── AppsFlyerLib.pdb │ │ │ ├── AppsFlyerLib.pdb.meta │ │ │ ├── AppsFlyerLib.pri │ │ │ ├── AppsFlyerLib.pri.meta │ │ │ ├── AppsFlyerLib.winmd │ │ │ ├── AppsFlyerLib.winmd.meta │ │ │ ├── AppsFlyerWindows.cs │ │ │ └── AppsFlyerWindows.cs.meta │ │ ├── Windows.meta │ │ ├── package.json │ │ └── package.json.meta │ └── AppsFlyer.meta ├── CHANGELOG.md ├── LICENSE ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── MemorySettings.asset │ ├── MultiplayerManager.asset │ ├── NavMeshAreas.asset │ ├── PackageManagerSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ ├── VersionControlSettings.asset │ └── XRSettings.asset ├── README.md ├── UserSettings/ │ ├── EditorUserSettings.asset │ ├── Layouts/ │ │ ├── default-2021.dwlt │ │ └── default-6000.dwlt │ └── Search.settings ├── android-unity-wrapper/ │ ├── .gitignore │ ├── app/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── appsflyer/ │ │ │ └── unity/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-v24/ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ └── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── appsflyer/ │ │ └── unity/ │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── unitywrapper/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── libs/ │ │ └── classes.jar │ ├── proguard-rules.pro │ ├── publish.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── appsflyer/ │ └── unity/ │ ├── AppsFlyerAndroidWrapper.java │ └── PurchaseRevenueBridge.java ├── appsflyer-unity-plugin-6.17.90.unitypackage ├── appsflyer-unity-plugin-6.17.91.unitypackage ├── deploy/ │ ├── build_unity_package.sh │ ├── external-dependency-manager-1.2.183.unitypackage │ └── strict_mode_build_package.sh ├── docs/ │ ├── API.md │ ├── BasicIntegration.md │ ├── DMAConsent.md │ ├── DeepLinkIntegrate.md │ ├── InAppEvents.md │ ├── Installation.md │ ├── Introduction.md │ ├── MigrationGuide.md │ ├── PushNotifications.md │ ├── Testing.md │ ├── Troubleshooting.md │ ├── UnifiedDeepLink.md │ ├── UninstallMeasurement.md │ ├── UserInvite.md │ ├── ad-revenue-unity.md │ ├── conversion-data-unity.md │ ├── purchase-connector.md │ ├── purchase-validation-unity.md │ └── validate-and-log-unity.md ├── obj/ │ └── Debug/ │ └── .NETFramework,Version=v4.7.1.AssemblyAttributes.cs └── strict-mode-sdk/ ├── appsflyer-unity-plugin-strict-mode-6.17.90.unitypackage ├── appsflyer-unity-plugin-strict-mode-6.17.91.unitypackage └── strictMode.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: AppsFlyer Support (Customer Assistant Chatbot) url: https://support.appsflyer.com/hc/en-us/articles/23583984402193-Using-the-Customer-Assistant-Chatbot about: For the fastest and most effective support, please contact the AppsFlyer support team using the Customer Assistant Chatbot. ================================================ FILE: .github/workflows/Scripts/checkJira.sh ================================================ JIRA_TOKEN=$1 JIRA_FIXED_VERSION=$2 fixed_version_found=false curl -X GET https://appsflyer.atlassian.net/rest/api/3/project/11723/versions --user $JIRA_TOKEN | jq -r '.[] | .name+""+(.id | tostring)' | while read version ; do if [[ "$version" == *"$JIRA_FIXED_VERSION"* ]] ;then echo "$JIRA_FIXED_VERSION Found!" fixed_version_found=true version_id=${version#"$JIRA_FIXED_VERSION"} echo $(curl -s -X POST https://appsflyer.atlassian.net/rest/api/3/search/jql --user $JIRA_TOKEN -H "Content-Type: application/json" -d "{\"jql\": \"fixVersion=$version_id\", \"fields\": [\"summary\"]}" | jq -r '.issues[] | "- " + .fields["summary"]+"@"') > "$JIRA_FIXED_VERSION-releasenotes".txt sed -i -r -e "s/@ /\n/gi" "$JIRA_FIXED_VERSION-releasenotes".txt sed -i -r -e "s/@/\n/gi" "$JIRA_FIXED_VERSION-releasenotes".txt cat "$JIRA_FIXED_VERSION-releasenotes".txt fi done if [ fixed_version_found == false ];then echo "$JIRA_FIXED_VERSION is not found!" exit 1 fi ================================================ FILE: .github/workflows/Scripts/extractPackage.py ================================================ from importlib.resources import path import re import hashlib import os import sys from unitypackage_extractor.extractor import extractPackage class checkPackage: def __init__(self, pathToPackage): self.pathToPackage = pathToPackage def extractPackage(self, pathToOuptut): extractPackage(self.pathToPackage, outputPath=pathToOuptut) def main(): package = checkPackage(sys.argv[1]) strict_package = checkPackage(sys.argv[2]) #testing integreity of files print("###################### \n Extracting files in unity packages \n ######################") package.extractPackage("./packageUnity") strict_package.extractPackage("./strictPackageUnity") path_of_the_strict_directory= 'strictPackageUnity/' path_of_the_directory= 'packageUnity/' path_of_repo = "Assets/" files_to_not_check = ["package.json"] files_for_strict_mode_only = ["AppsFlyeriOSWrapper.mm", "AppsFlyerDependencies.xml"] #checksum of files print("###################### \n testing integreity of files \n ######################") package.extractPackage("./packageUnity") for subdir, dirs, files in os.walk(path_of_repo): for file in files: file_in_package = os.path.join(*[path_of_the_directory, subdir,file]) file_in_repo = os.path.join(subdir, file) file_in_strict_package = os.path.join(*[path_of_the_strict_directory, subdir,file]) if os.path.isfile(file_in_package) and os.path.isfile(file_in_repo) and os.path.isfile(file_in_strict_package): if file in files_to_not_check: continue if file in files_for_strict_mode_only: if getHash(file_in_package) != getHash(file_in_repo): print("❌ the file ", file, "is not the same") sys.exit(5) print("file for non strict mode ", file, "md5 check passed ✅") if file == "AppsFlyeriOSWrapper.mm": if not hasCommentedMethods(file_in_strict_package): print("❌ the methods are not commented in ", file_in_strict_package) sys.exit(5) print("file in strict mode ", file, " has the correct methods commented out ✅") if file == "AppsFlyerDependencies.xml": if not isSrictModeDependency(file_in_strict_package): print("❌ the dependecy is not strict in ",file_in_strict_package ) sys.exit(5) print("file in strict mode ", file, " has the correct depdendency ✅") else: if getHash(file_in_package) != getHash(file_in_repo) or getHash(file_in_repo) != getHash(file_in_strict_package): print("❌ the file" , file, "is not the same") sys.exit(5) print("file ", file, "md5 check passed ✅") def getHash(filePath): md5 = hashlib.md5() with open(filePath,'rb') as file: hash = file.read() md5.update(hash) return md5.hexdigest() #check that only the two methods are commented in the strict mode package def hasCommentedMethods(file): print("###################### \n checking that the methods are commented in the strict mode package \n ######################") textfile = open(file, 'r') filetext = textfile.read() textfile.close() matches1 = re.findall("[/]+.*\[+AppsFlyerLib.*disableAdvertisingIdentifier", filetext) matches2 = re.findall("[/]+.*\[+AppsFlyerLib.*waitForATTUserAuthorizationWithTimeoutInterval", filetext) return len(matches1) == 1 and len(matches2) == 1 #check that we are using the strict dependency in strict mode package def isSrictModeDependency(file): print("###################### \nchecking the depdendency for the strict mode \n ######################") textfile = open(file, 'r') filetext = textfile.read() textfile.close() match = re.findall("AppsFlyerFramework/Strict", filetext) return len(match)>0 if __name__ == "__main__": main() ================================================ FILE: .github/workflows/activation.yml ================================================ name: Acquire activation file for Unity on: push: branches: - dev/RD-81493/unit-tests jobs: activation: name: Request manual activation file 🔑 runs-on: ubuntu-latest steps: # Request manual activation file - name: Request manual activation file id: getManualLicenseFile uses: game-ci/unity-request-activation-file@v2 # Upload artifact (Unity_v20XX.X.XXXX.alf) - name: Expose as artifact uses: actions/upload-artifact@v2 with: name: ${{ steps.getManualLicenseFile.outputs.filePath }} path: ${{ steps.getManualLicenseFile.outputs.filePath }} ================================================ FILE: .github/workflows/check_packages.yml ================================================ name: check packages and release to upm on: workflow_call: inputs: plugin_version: required: true type: string jobs: check-packages: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: get version run: | echo "PLUGIN_VERSION=${{ inputs.plugin_version }}" >> $GITHUB_ENV - name: setup python uses: actions/setup-python@v4 with: python-version: '3.9' - name: install python packages run: | python -m pip install --upgrade pip pip install unitypackage_extractor - name: execute py script run: python .github/workflows/Scripts/extractPackage.py appsflyer-unity-plugin-${{env.PLUGIN_VERSION}}.unitypackage strict-mode-sdk/appsflyer-unity-plugin-strict-mode-${{env.PLUGIN_VERSION}}.unitypackage release-upm: runs-on: ubuntu-latest needs: check-packages steps: - uses: actions/checkout@v4 - name: split branch env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | git branch -d upm &> /dev/null || echo upm branch not found git subtree split -P Assets/AppsFlyer -b upm git checkout upm echo "Checked out to upm branch" git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL git rm -r Tests git commit -m "Remove Tests folder from upm branch" echo "Removed Tests folder" git push -f -u origin upm - name: create tag run: | git tag v${{inputs.plugin_version}} git push origin v${{inputs.plugin_version}} echo "Release v${{inputs.plugin_version}} to upm :bookmark:" strict-upm: runs-on: ubuntu-latest needs: check-packages steps: - uses: actions/checkout@v4 - name: split branch env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | git branch -d Strict-upm &> /dev/null || echo Strict-upm branch not found git subtree split -P Assets/AppsFlyer -b Strict-upm git checkout Strict-upm echo "Checked out to Strict-upm branch" git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL git rm -r Tests git commit -m "Remove Tests folder from Strict-upm branch" echo "Removed Tests folder" - name: Modify for strict mode run: | echo "Changing AppsFlyerFramework to Strict Mode" grep -q "AppsFlyerFramework" Editor/AppsFlyerDependencies.xml sed -i 's/AppsFlyerFramework/AppsFlyerFramework\/Strict/g' Editor/AppsFlyerDependencies.xml echo "Changing package.json to Strict Mode" grep -q "AppsFlyer Unity plugin" ./package.json sed -i 's/AppsFlyer Unity plugin/AppsFlyer Unity plugin strict mode/g' ./package.json echo "Commenting out disableAdvertisingIdentifier" grep -q "\[AppsFlyerLib shared\].disableAdvertisingIdentifier" Plugins/iOS/AppsFlyeriOSWrapper.mm sed -i 's/\[AppsFlyerLib shared\].disableAdvertisingIdentifier/\/\/\[AppsFlyerLib shared\].disableAdvertisingIdentifier/g' Plugins/iOS/AppsFlyeriOSWrapper.mm echo "Commenting out waitForATTUserAuthorizationWithTimeoutInterval" grep -q "\[\[AppsFlyerLib shared\] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval\];" Plugins/iOS/AppsFlyeriOSWrapper.mm sed -i 's/\[\[AppsFlyerLib shared\] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval\];/\/\/\[\[AppsFlyerLib shared\] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval\];/g' Plugins/iOS/AppsFlyeriOSWrapper.mm - name: Commit changes and push env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | git config user.name $COMMIT_AUTHOR git config user.email $COMMIT_EMAIL git add -A git commit -m "update package.json" git push -f -u origin Strict-upm - name: create tag run: | git tag Strict-v${{inputs.plugin_version}} git push origin Strict-v${{inputs.plugin_version}} echo "Release Strict mode v${{inputs.plugin_version}} to upm :bookmark:" ================================================ FILE: .github/workflows/close_inactive_issues.yml ================================================ # This workflow triggers the org-wide reusable workflow to close inactive issues on a schedule on: schedule: - cron: "0 10 * * *" # Runs daily at 10:00 UTC workflow_dispatch: jobs: close-issues: uses: AppsFlyerSDK/github-common-workflow-and-template/.github/workflows/close_inactive_issues.yml@main secrets: inherit ================================================ FILE: .github/workflows/issues_workflow.yml ================================================ name: Close inactive issues on: schedule: - cron: "30 9 * * 0" jobs: close-issues: runs-on: ubuntu-latest permissions: issues: write pull-requests: write steps: - uses: actions/stale@v5 with: days-before-issue-stale: 30 days-before-issue-close: 14 stale-issue-label: "stale" stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." days-before-pr-stale: -1 days-before-pr-close: -1 repo-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/main.yml ================================================ name: Test runner unity-appsflyer-plugin on: workflow_call: jobs: run-unity-tests: name: Run ${{ matrix.testMode }} tests for ${{ matrix.targetPlatform }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: testMode: [playmode] targetPlatform: [iOS, Android, Shared] steps: - uses: actions/checkout@v2 with: lfs: true - name: Free up disk space run: | rm -rf /usr/share/dotnet/ rm -rf "/usr/local/share/boost" rm -rf "$AGENT_TOOLSDIRECTORY" rm -rf /opt/ghc - uses: actions/cache@v4 with: path: Library key: Library-${{ matrix.targetPlatform }}-${{ matrix.testMode }}-${{ hashFiles('**/*.cs') }} restore-keys: | Library-${{ matrix.targetPlatform }}-${{ matrix.testMode }}- Library-${{ matrix.targetPlatform }}- Library- - uses: game-ci/unity-test-runner@v4 id: tests env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} with: projectPath: . testMode: ${{ matrix.testMode }} artifactsPath: ${{ matrix.targetPlatform }}-artifacts githubToken: ${{ secrets.GITHUB_TOKEN }} checkName: ${{ matrix.targetPlatform }} Test Results customParameters: > -nographics -buildTarget ${{ matrix.targetPlatform == 'Shared' && 'StandaloneLinux64' || matrix.targetPlatform }} -defineSymbols ${{ matrix.targetPlatform == 'Shared' && '' || format('UNITY_{0}', matrix.targetPlatform) }} - name: Upload test results if: always() uses: actions/upload-artifact@v4 with: name: Test results - ${{ matrix.targetPlatform }} path: ${{ steps.tests.outputs.artifactsPath }} ================================================ FILE: .github/workflows/readme_sync.yml ================================================ name: Sync `docs` directory to ReadMe # Run workflow for every push to the `main` branch on: push: branches: - master jobs: sync: runs-on: ubuntu-latest steps: - name: Checkout this repo uses: actions/checkout@v3 # Run GitHub Action to sync docs in `documentation` directory - name: GitHub Action # We recommend specifying a fixed version, i.e. @7.5.0 # Docs: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#example-using-versioned-actions uses: readmeio/rdme@7.5.0 with: rdme: docs ./docs --key=${{ secrets.README_SYNC_API_KEY }} --version=0.1 ================================================ FILE: .github/workflows/release_QA_workflow.yml ================================================ name: plugin release to QA on: push: branches: - releases/[0-9]+.x.x/[0-9]+.[0-9]+.x/[0-9]+.[0-9]+.[0-9]+-rc[0-9]+ paths-ignore: - master jobs: Run-Unit-Tests: uses: ./.github/workflows/main.yml secrets: inherit Release-To-QA: needs: [Run-Unit-Tests] uses: ./.github/workflows/release_to_QA.yml secrets: inherit ================================================ FILE: .github/workflows/release_production_workflow.yml ================================================ name: plugin release to Production workflow on: pull_request: types: - closed branches: - 'master' paths-ignore: - '**.md' - 'docs/**.md' - '**.sh' jobs: Prepare-Release: if: github.event.pull_request.merged == true runs-on: ubuntu-latest outputs: output1: ${{ steps.tag-step.outputs.PLUGIN_VERSION}} output2: ${{ steps.tag-step.outputs.RELEASE_BRANCH_NAME}} steps: - uses: actions/checkout@v3 - name: Login to GitHub env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME}} COMMIT_EMAIL: ${{ secrets.CI_EMAIL}} run: | git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL - uses: mdecoleman/pr-branch-name@1.2.0 id: vars with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Determine release tag and release branch id: tag-step run: | TAG=$(echo "${{ steps.vars.outputs.branch }}" | grep -Eo '[0-9]+.[0-9]+.[0-9]+') echo "PLUGIN_VERSION=$TAG" >> $GITHUB_ENV echo "PLUGIN_VERSION=$TAG" >> $GITHUB_OUTPUT echo "RELEASE_BRANCH_NAME=${{ steps.vars.outputs.branch }}" >> $GITHUB_ENV echo "RELEASE_BRANCH_NAME=${{ steps.vars.outputs.branch }}" >> $GITHUB_OUTPUT Check-Packages: needs: [Prepare-Release] uses: ./.github/workflows/check_packages.yml with: plugin_version: ${{ needs.Prepare-Release.outputs.output1 }} secrets: inherit Update-Links: needs: [Prepare-Release, Check-Packages] uses: ./.github/workflows/update_links.yml secrets: inherit Generate-Tag: needs: [Prepare-Release, Check-Packages, Update-Links] runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Create a version tag and push it run: | echo "Pushing release tag: ${{ needs.Prepare-Release.outputs.output1 }}!" git checkout master echo "Checked out to master" git pull origin master git tag ${{ needs.Prepare-Release.outputs.output1 }} echo "Created release tag" git push origin ${{needs.Prepare-Release.outputs.output1}} echo "Pushed release tag ${{ needs.Prepare-Release.outputs.output1 }}" Release-To-Production: needs: [Prepare-Release, Check-Packages, Update-Links, Generate-Tag] uses: ./.github/workflows/release_to_production.yml with: plugin_version: ${{ needs.Prepare-Release.outputs.output1 }} branch_name: ${{ needs.Prepare-Release.outputs.output2 }} secrets: inherit ================================================ FILE: .github/workflows/release_to_QA.yml ================================================ name: Release to QA on: workflow_call: jobs: Release-To-QA: runs-on: ubuntu-latest environment: Staging steps: - uses: actions/checkout@v3 - name: Login to Github env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL - name: Check fixed version and compare issues on Jira env: JIRA_TOKEN: ${{ secrets.CI_JIRA_TOKEN }} BRANCH_NAME: ${{ github.ref_name }} run: | plugin_version=$(echo "$BRANCH_NAME" | grep -Eo '[0-9]+.[0-9]+.[0-9]+') jira_fixed_version="Unity SDK v$plugin_version" echo "JIRA_FIXED_VERSION=$jira_fixed_version" >> $GITHUB_ENV chmod +x .github/workflows/Scripts/checkJira.sh .github/workflows/Scripts/checkJira.sh $JIRA_TOKEN "$jira_fixed_version" - name: Update package.json env: BRANCH_NAME: ${{ github.ref_name }} COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | echo "updating package.json version" plugin_version=$(echo "$BRANCH_NAME" | grep -Eo '[0-9]+.[0-9]+.[0-9]+') sed -E -i "s/\"version\": \"[0-9]+.[0-9]+.[0-9]+\"/\"version\": \"$plugin_version\"/g" Assets/AppsFlyer/package.json if [[ -n $(git status -s) ]]; then echo "Commit and push" git add Assets/AppsFlyer/package.json git commit -m "update package.json" git push origin $BRANCH_NAME else echo "package.json version is already up to date" fi - name: Send slack report env: SLACK_TOKEN: ${{ secrets.CI_SLACK_TOKEN }} run: | ios_af_sdk_version=$(cat README.md | grep -Eo 'iOS AppsFlyer SDK v[0-9]+.[0-9]+.[0-9]+') android_af_sdk_version=$(cat README.md | grep -Eo 'Android AppsFlyer SDK v[0-9]+.[0-9]+.[0-9]+') # Extract plugin version from branch name plugin_version=$(echo "${{github.ref_name}}" | grep -Eo '[0-9]+.[0-9]+.[0-9]+') # Send webhook to Slack with all required parameters curl -X POST -H 'Content-type: application/json' --data '{ "plugin_version": "'"$plugin_version"'", "git_branch": "'"${{github.ref_name}}"'", "ios_sdk_dependency": "'"$ios_af_sdk_version"'", "android_sdk_dependency": "'"$android_af_sdk_version"'", "version_changes": "New Unity Plugin Release: Unity SDK v'"$plugin_version"'", "deploy_type": "QA" }' "$SLACK_TOKEN" ================================================ FILE: .github/workflows/release_to_production.yml ================================================ name: plugin release to Production on: workflow_call: inputs: plugin_version: required: true type: string branch_name: required: true type: string jobs: Release-To-Production: runs-on: ubuntu-latest environment: Production steps: - uses: actions/checkout@v3 - name: Login to Github env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL - name: Create GitHub Release env: VTAG: v${{ inputs.plugin_version }} TAG: ${{ inputs.plugin_version }} uses: "actions/github-script@v5" with: github-token: "${{ secrets.GITHUB_TOKEN }}" script: | try { await github.rest.repos.createRelease({ draft: false, generate_release_notes: false, name: process.env.VTAG, owner: context.repo.owner, prerelease: false, repo: context.repo.repo, tag_name: process.env.TAG }); } catch (error) { core.setFailed(error.message); } - name: Send slack report env: SLACK_TOKEN: ${{ secrets.CI_SLACK_TOKEN }} JIRA_TOKEN: ${{ secrets.CI_JIRA_TOKEN }} run: | jira_fixed_version="Unity SDK v${{inputs.plugin_version}}" chmod +x .github/workflows/Scripts/checkJira.sh .github/workflows/Scripts/checkJira.sh $JIRA_TOKEN "$jira_fixed_version" ios_af_sdk_version=$(cat README.md | grep -Eo 'iOS AppsFlyer SDK v[0-9]+.[0-9]+.[0-9]+') android_af_sdk_version=$(cat README.md | grep -Eo 'Android AppsFlyer SDK v[0-9]+.[0-9]+.[0-9]+') # Create a simple release message without the text file release_message="🚀 PRODUCTION RELEASE: Unity Plugin ${{inputs.plugin_version}}" # Send webhook to Slack with all required parameters curl -X POST -H 'Content-type: application/json' --data '{ "plugin_version": "'"${{inputs.plugin_version}}"'", "git_branch": "'"${{inputs.branch_name}}"'", "ios_sdk_dependency": "'"$ios_af_sdk_version"'", "android_sdk_dependency": "'"$android_af_sdk_version"'", "version_changes": "🚀 PRODUCTION RELEASE: Unity Plugin ${{inputs.plugin_version}}", "deploy_type": "PRODUCTION" }' "$SLACK_TOKEN" ================================================ FILE: .github/workflows/responseToSupportIssue.yml ================================================ # This workflow triggers the org-wide reusable workflow to respond to issues labeled as 'support' on: issues: types: - labeled workflow_dispatch: jobs: add-comment: uses: AppsFlyerSDK/github-common-workflow-and-template/.github/workflows/responseToSupportIssue.yml@main secrets: inherit ================================================ FILE: .github/workflows/responseToSupportIssueOnOpen.yml ================================================ # This workflow triggers the org-wide reusable workflow to respond to newly opened issues on: issues: types: - opened workflow_dispatch: jobs: add-comment: uses: AppsFlyerSDK/github-common-workflow-and-template/.github/workflows/responseToSupportIssueOnOpen.yml@main secrets: inherit ================================================ FILE: .github/workflows/test-unity-auth.yml ================================================ name: Test Unity Authentication on: workflow_dispatch: jobs: test-auth: name: Test Unity License Activation runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Test Unity activation uses: game-ci/unity-activate@v2 env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - name: Success message run: echo "Unity authentication successful!" ================================================ FILE: .github/workflows/update_links.yml ================================================ name: update sdk links on: workflow_call: jobs: update-links: runs-on: ubuntu-latest steps: - name: Check out repo uses: actions/checkout@v4 - name: Update links id: update-links env: COMMIT_AUTHOR: ${{ secrets.CI_USERNAME }} COMMIT_EMAIL: ${{ secrets.CI_EMAIL }} run: | echo "Getting the versions" grep -o 'spec="com.appsflyer:[^"]\+"' Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml | sed -E 's/spec="com.appsflyer:([^"]+)"/\1/' | awk -F':' '{print $2}' > output.txt grep -o 'name="AppsFlyerFramework" version="[^"]\+"' Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml | sed -E 's/name="AppsFlyerFramework" version="([^"]+)"/\1/' >> output.txt echo "Changing links" android_version=$(sed -n '1p' output.txt) sed -E -i "s/af-android-sdk\/[0-9]+.[0-9]+.[0-9]+/af-android-sdk\/$android_version/g" docs/Installation.md sed -E -i "s/af-android-sdk-[0-9]+.[0-9]+.[0-9]+/af-android-sdk-$android_version/g" docs/Installation.md wrapper_version=$(sed -n '2p' output.txt) sed -E -i "s/unity-wrapper\/[0-9]+.[0-9]+.[0-9]+/unity-wrapper\/$wrapper_version/g" docs/Installation.md sed -E -i "s/unity-wrapper-[0-9]+.[0-9]+.[0-9]+/unity-wrapper-$wrapper_version/g" docs/Installation.md ios_version=$(sed -n '3p' output.txt) major_version="${ios_version%%.*}" minor_version="${ios_version%.*}" sed -E -i "s/appsflyer.com\/ios\/[0-9]+.x.x\/[0-9]+.[0-9]+.x\/[0-9]+.[0-9]+.[0-9]+\/AF-iOS-SDK-v[0-9]+.[0-9]+.[0-9]+/appsflyer.com\/ios\/$major_version.x.x\/$minor_version.x\/$ios_version\/AF-iOS-SDK-v$ios_version/g" docs/Installation.md sed -E -i "s/unity-wrapper-[0-9]+.[0-9]+.[0-9]+/unity-wrapper-$wrapper_version/g" docs/Installation.md rm output.txt if [[ -n $(git status -s) ]]; then echo "Links need updating, will create PR" git config --global user.name $COMMIT_AUTHOR git config --global user.email $COMMIT_EMAIL git add docs/Installation.md git commit -m "generate links" echo "changes_made=true" >> $GITHUB_OUTPUT else echo "Links are already up to date" echo "changes_made=false" >> $GITHUB_OUTPUT fi - name: Create Pull Request if: steps.update-links.outputs.changes_made == 'true' uses: peter-evans/create-pull-request@v5 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "generate links" title: "Update SDK links for version ${{ inputs.plugin_version || 'latest' }}" body: | This PR automatically updates the SDK links in the Installation.md file. **Changes:** - Updated Android SDK links - Updated iOS SDK links - Updated Unity wrapper links This PR was created automatically by the release workflow. branch: update-links-${{ github.run_number }} base: master delete-branch: true ================================================ FILE: .gitignore ================================================ .DS_Store *.log Logs/ Packages/ Temp/ Library/ /*/outputs/ #android studio build gen .gradle .idea .hgcheck infer-out captures local.properties *.orig *.rej out bin dist *.iml build.gradle_mine .DS_Store *.idea .vs/appsflyer-unity-plugin/xs/project-cache/ .vs/appsflyer-unity-plugin/xs/ .vsconfig *.sln *.csproj *.cache ================================================ FILE: Assets/AppsFlyer/AFAdRevenueData.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { public enum MediationNetwork : ulong { GoogleAdMob = 1, IronSource = 2, ApplovinMax = 3, Fyber = 4, Appodeal = 5, Admost = 6, Topon = 7, Tradplus = 8, Yandex = 9, ChartBoost = 10, Unity = 11, ToponPte = 12, Custom = 13, DirectMonetization = 14 } public static class AdRevenueScheme { /** * code ISO 3166-1 format */ public const string COUNTRY = "country"; /** * ID of the ad unit for the impression */ public const string AD_UNIT = "ad_unit"; /** * Format of the ad */ public const string AD_TYPE = "ad_type"; /** * ID of the ad placement for the impression */ public const string PLACEMENT = "placement"; } /// // Data class representing ad revenue information. // // @property monetizationNetwork The name of the network that monetized the ad. // @property mediationNetwork An instance of MediationNetwork representing the mediation service used. // @property currencyIso4217Code The ISO 4217 currency code describing the currency of the revenue. // @property eventRevenue The amount of revenue generated by the ad. /// public class AFAdRevenueData { public string monetizationNetwork { get; private set; } public MediationNetwork mediationNetwork { get; private set; } public string currencyIso4217Code { get; private set; } public double eventRevenue { get; private set; } public AFAdRevenueData(string monetization, MediationNetwork mediation, string currency, double revenue) { monetizationNetwork = monetization; mediationNetwork = mediation; currencyIso4217Code = currency; eventRevenue = revenue; } } } ================================================ FILE: Assets/AppsFlyer/AFAdRevenueData.cs.meta ================================================ fileFormatVersion: 2 guid: 49e1906ae949e4bfea400bd1da9f7e39 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AFInAppEvents.cs ================================================ using UnityEngine; using System.Collections; public class AFInAppEvents { /** * Event Type * */ public const string LEVEL_ACHIEVED = "af_level_achieved"; public const string ADD_PAYMENT_INFO = "af_add_payment_info"; public const string ADD_TO_CART = "af_add_to_cart"; public const string ADD_TO_WISH_LIST = "af_add_to_wishlist"; public const string COMPLETE_REGISTRATION = "af_complete_registration"; public const string TUTORIAL_COMPLETION = "af_tutorial_completion"; public const string INITIATED_CHECKOUT = "af_initiated_checkout"; public const string PURCHASE = "af_purchase"; public const string RATE = "af_rate"; public const string SEARCH = "af_search"; public const string SPENT_CREDIT = "af_spent_credits"; public const string ACHIEVEMENT_UNLOCKED = "af_achievement_unlocked"; public const string CONTENT_VIEW = "af_content_view"; public const string TRAVEL_BOOKING = "af_travel_booking"; public const string SHARE = "af_share"; public const string INVITE = "af_invite"; public const string LOGIN = "af_login"; public const string RE_ENGAGE = "af_re_engage"; public const string UPDATE = "af_update"; public const string OPENED_FROM_PUSH_NOTIFICATION = "af_opened_from_push_notification"; public const string LOCATION_CHANGED = "af_location_changed"; public const string LOCATION_COORDINATES = "af_location_coordinates"; public const string ORDER_ID = "af_order_id"; /** * Event Parameter Name * **/ public const string LEVEL = "af_level"; public const string SCORE = "af_score"; public const string SUCCESS = "af_success"; public const string PRICE = "af_price"; public const string CONTENT_TYPE = "af_content_type"; public const string CONTENT_ID = "af_content_id"; public const string CONTENT_LIST = "af_content_list"; public const string CURRENCY = "af_currency"; public const string QUANTITY = "af_quantity"; public const string REGSITRATION_METHOD = "af_registration_method"; public const string PAYMENT_INFO_AVAILIBLE = "af_payment_info_available"; public const string MAX_RATING_VALUE = "af_max_rating_value"; public const string RATING_VALUE = "af_rating_value"; public const string SEARCH_STRING = "af_search_string"; public const string DATE_A = "af_date_a"; public const string DATE_B = "af_date_b"; public const string DESTINATION_A = "af_destination_a"; public const string DESTINATION_B = "af_destination_b"; public const string DESCRIPTION = "af_description"; public const string CLASS = "af_class"; public const string EVENT_START = "af_event_start"; public const string EVENT_END = "af_event_end"; public const string LATITUDE = "af_lat"; public const string LONGTITUDE = "af_long"; public const string CUSTOMER_USER_ID = "af_customer_user_id"; public const string VALIDATED = "af_validated"; public const string REVENUE = "af_revenue"; public const string RECEIPT_ID = "af_receipt_id"; public const string PARAM_1 = "af_param_1"; public const string PARAM_2 = "af_param_2"; public const string PARAM_3 = "af_param_3"; public const string PARAM_4 = "af_param_4"; public const string PARAM_5 = "af_param_5"; public const string PARAM_6 = "af_param_6"; public const string PARAM_7 = "af_param_7"; public const string PARAM_8 = "af_param_8"; public const string PARAM_9 = "af_param_9"; public const string PARAM_10 = "af_param_10"; } ================================================ FILE: Assets/AppsFlyer/AFInAppEvents.cs.meta ================================================ fileFormatVersion: 2 guid: 4075c6cf6f3d94b9a9f37f826e6a0e6f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AFMiniJSON.cs ================================================ /* * Copyright (c) 2013 Calvin Rien * * Based on the JSON parser by Patrick van Bergen * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html * * Simplified it so that it doesn't throw exceptions * and can be used in Unity iPhone with maximum code stripping. * * 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. */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; namespace AFMiniJSON { // Example usage: // // using UnityEngine; // using System.Collections; // using System.Collections.Generic; // using MiniJSON; // // public class MiniJSONTest : MonoBehaviour { // void Start () { // var jsonString = "{ \"array\": [1.44,2,3], " + // "\"object\": {\"key1\":\"value1\", \"key2\":256}, " + // "\"string\": \"The quick brown fox \\\"jumps\\\" over the lazy dog \", " + // "\"unicode\": \"\\u3041 Men\u00fa sesi\u00f3n\", " + // "\"int\": 65536, " + // "\"float\": 3.1415926, " + // "\"bool\": true, " + // "\"null\": null }"; // // var dict = Json.Deserialize(jsonString) as Dictionary; // // Debug.Log("deserialized: " + dict.GetType()); // Debug.Log("dict['array'][0]: " + ((List) dict["array"])[0]); // Debug.Log("dict['string']: " + (string) dict["string"]); // Debug.Log("dict['float']: " + (double) dict["float"]); // floats come out as doubles // Debug.Log("dict['int']: " + (long) dict["int"]); // ints come out as longs // Debug.Log("dict['unicode']: " + (string) dict["unicode"]); // // var str = Json.Serialize(dict); // // Debug.Log("serialized: " + str); // } // } /// /// This class encodes and decodes JSON strings. /// Spec. details, see http://www.json.org/ /// /// JSON uses Arrays and Objects. These correspond here to the datatypes IList and IDictionary. /// All numbers are parsed to doubles. /// public static class Json { /// /// Parses the string json into a value /// /// A JSON string. /// An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false public static object Deserialize(string json) { // save the string for debug information if (json == null) { return null; } return Parser.Parse(json); } sealed class Parser : IDisposable { const string WORD_BREAK = "{}[],:\""; public static bool IsWordBreak(char c) { return Char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1; } enum TOKEN { NONE, CURLY_OPEN, CURLY_CLOSE, SQUARED_OPEN, SQUARED_CLOSE, COLON, COMMA, STRING, NUMBER, TRUE, FALSE, NULL }; StringReader json; Parser(string jsonString) { json = new StringReader(jsonString); } public static object Parse(string jsonString) { using (var instance = new Parser(jsonString)) { return instance.ParseValue(); } } public void Dispose() { json.Dispose(); json = null; } Dictionary ParseObject() { Dictionary table = new Dictionary(); // ditch opening brace json.Read(); // { while (true) { switch (NextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.CURLY_CLOSE: return table; default: // name string name = ParseString(); if (name == null) { return null; } // : if (NextToken != TOKEN.COLON) { return null; } // ditch the colon json.Read(); // value table[name] = ParseValue(); break; } } } List ParseArray() { List array = new List(); // ditch opening bracket json.Read(); // [ var parsing = true; while (parsing) { TOKEN nextToken = NextToken; switch (nextToken) { case TOKEN.NONE: return null; case TOKEN.COMMA: continue; case TOKEN.SQUARED_CLOSE: parsing = false; break; default: object value = ParseByToken(nextToken); array.Add(value); break; } } return array; } object ParseValue() { TOKEN nextToken = NextToken; return ParseByToken(nextToken); } object ParseByToken(TOKEN token) { switch (token) { case TOKEN.STRING: return ParseString(); case TOKEN.NUMBER: return ParseNumber(); case TOKEN.CURLY_OPEN: return ParseObject(); case TOKEN.SQUARED_OPEN: return ParseArray(); case TOKEN.TRUE: return true; case TOKEN.FALSE: return false; case TOKEN.NULL: return null; default: return null; } } string ParseString() { StringBuilder s = new StringBuilder(); char c; // ditch opening quote json.Read(); bool parsing = true; while (parsing) { if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': parsing = false; break; case '\\': if (json.Peek() == -1) { parsing = false; break; } c = NextChar; switch (c) { case '"': case '\\': case '/': s.Append(c); break; case 'b': s.Append('\b'); break; case 'f': s.Append('\f'); break; case 'n': s.Append('\n'); break; case 'r': s.Append('\r'); break; case 't': s.Append('\t'); break; case 'u': var hex = new char[4]; for (int i=0; i< 4; i++) { hex[i] = NextChar; } s.Append((char) Convert.ToInt32(new string(hex), 16)); break; } break; default: s.Append(c); break; } } return s.ToString(); } object ParseNumber() { string number = NextWord; if (number.IndexOf('.') == -1) { long parsedInt; Int64.TryParse(number, out parsedInt); return parsedInt; } double parsedDouble; Double.TryParse(number, out parsedDouble); return parsedDouble; } void EatWhitespace() { while (Char.IsWhiteSpace(PeekChar)) { json.Read(); if (json.Peek() == -1) { break; } } } char PeekChar { get { return Convert.ToChar(json.Peek()); } } char NextChar { get { return Convert.ToChar(json.Read()); } } string NextWord { get { StringBuilder word = new StringBuilder(); while (!IsWordBreak(PeekChar)) { word.Append(NextChar); if (json.Peek() == -1) { break; } } return word.ToString(); } } TOKEN NextToken { get { EatWhitespace(); if (json.Peek() == -1) { return TOKEN.NONE; } switch (PeekChar) { case '{': return TOKEN.CURLY_OPEN; case '}': json.Read(); return TOKEN.CURLY_CLOSE; case '[': return TOKEN.SQUARED_OPEN; case ']': json.Read(); return TOKEN.SQUARED_CLOSE; case ',': json.Read(); return TOKEN.COMMA; case '"': return TOKEN.STRING; case ':': return TOKEN.COLON; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': return TOKEN.NUMBER; } switch (NextWord) { case "false": return TOKEN.FALSE; case "true": return TOKEN.TRUE; case "null": return TOKEN.NULL; } return TOKEN.NONE; } } } /// /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string /// /// A Dictionary<string, object> / List<object> /// A JSON encoded string, or null if object 'json' is not serializable public static string Serialize(object obj) { return Serializer.Serialize(obj); } sealed class Serializer { StringBuilder builder; Serializer() { builder = new StringBuilder(); } public static string Serialize(object obj) { var instance = new Serializer(); instance.SerializeValue(obj); return instance.builder.ToString(); } void SerializeValue(object value) { IList asList; IDictionary asDict; string asStr; if (value == null) { builder.Append("null"); } else if ((asStr = value as string) != null) { SerializeString(asStr); } else if (value is bool) { builder.Append((bool) value ? "true" : "false"); } else if ((asList = value as IList) != null) { SerializeArray(asList); } else if ((asDict = value as IDictionary) != null) { SerializeObject(asDict); } else if (value is char) { SerializeString(new string((char) value, 1)); } else { SerializeOther(value); } } void SerializeObject(IDictionary obj) { bool first = true; builder.Append('{'); foreach (object e in obj.Keys) { if (!first) { builder.Append(','); } SerializeString(e.ToString()); builder.Append(':'); SerializeValue(obj[e]); first = false; } builder.Append('}'); } void SerializeArray(IList anArray) { builder.Append('['); bool first = true; foreach (object obj in anArray) { if (!first) { builder.Append(','); } SerializeValue(obj); first = false; } builder.Append(']'); } void SerializeString(string str) { builder.Append('\"'); char[] charArray = str.ToCharArray(); foreach (var c in charArray) { switch (c) { case '"': builder.Append("\\\""); break; case '\\': builder.Append("\\\\"); break; case '\b': builder.Append("\\b"); break; case '\f': builder.Append("\\f"); break; case '\n': builder.Append("\\n"); break; case '\r': builder.Append("\\r"); break; case '\t': builder.Append("\\t"); break; default: int codepoint = Convert.ToInt32(c); if ((codepoint >= 32) && (codepoint <= 126)) { builder.Append(c); } else { builder.Append("\\u"); builder.Append(codepoint.ToString("x4")); } break; } } builder.Append('\"'); } void SerializeOther(object value) { // NOTE: decimals lose precision during serialization. // They always have, I'm just letting you know. // Previously floats and doubles lost precision too. if (value is float) { builder.Append(((float) value).ToString("R")); } else if (value is int || value is uint || value is long || value is sbyte || value is byte || value is short || value is ushort || value is ulong) { builder.Append(value); } else if (value is double || value is decimal) { builder.Append(Convert.ToDouble(value).ToString("R")); } else { SerializeString(value.ToString()); } } } } } ================================================ FILE: Assets/AppsFlyer/AFMiniJSON.cs.meta ================================================ fileFormatVersion: 2 guid: bc3d1c806d507463e9b560eb09d8eb0e MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AFPurchaseDetailsAndroid.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { public enum AFPurchaseType { Subscription = 0, OneTimePurchase = 1 } /// /// Purchase details class matching Android SDK AFPurchaseDetails /// public class AFPurchaseDetailsAndroid { public AFPurchaseType purchaseType { get; private set; } public string purchaseToken { get; private set; } public string productId { get; private set; } public AFPurchaseDetailsAndroid(AFPurchaseType type, String purchaseToken, String productId) { this.purchaseType = type; this.purchaseToken = purchaseToken; this.productId = productId; } } } ================================================ FILE: Assets/AppsFlyer/AFPurchaseDetailsAndroid.cs.meta ================================================ fileFormatVersion: 2 guid: d71b3864006f94ac08938b2ebdc940bc MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AFSDKPurchaseDetailsIOS.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { /// /// Purchase type enum matching iOS SDK AFSDKPurchaseType /// public enum AFSDKPurchaseType { Subscription, OneTimePurchase } /// /// Purchase details class matching iOS SDK AFSDKPurchaseDetails /// public class AFSDKPurchaseDetailsIOS { public string productId { get; private set; } public string transactionId { get; private set; } public AFSDKPurchaseType purchaseType { get; private set; } private AFSDKPurchaseDetailsIOS(string productId, string transactionId, AFSDKPurchaseType purchaseType) { this.productId = productId; this.transactionId = transactionId; this.purchaseType = purchaseType; } public static AFSDKPurchaseDetailsIOS Init(string productId, string transactionId, AFSDKPurchaseType purchaseType) { return new AFSDKPurchaseDetailsIOS(productId, transactionId, purchaseType); } } } ================================================ FILE: Assets/AppsFlyer/AFSDKPurchaseDetailsIOS.cs.meta ================================================ fileFormatVersion: 2 guid: 44bb6c4472701416080eb050732075ea MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AFSDKValidateAndLogResult.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { public enum AFSDKValidateAndLogStatus { AFSDKValidateAndLogStatusSuccess, AFSDKValidateAndLogStatusFailure, AFSDKValidateAndLogStatusError } /// // /// public class AFSDKValidateAndLogResult { public AFSDKValidateAndLogStatus status { get; private set; } public Dictionary result { get; private set; } public Dictionary errorData { get; private set; } public string error { get; private set; } private AFSDKValidateAndLogResult(AFSDKValidateAndLogStatus status, Dictionary result, Dictionary errorData, string error) { this.status = status; this.result = result; this.errorData = errorData; this.error = error; } public static AFSDKValidateAndLogResult Init(AFSDKValidateAndLogStatus status, Dictionary result, Dictionary errorData, string error) { return new AFSDKValidateAndLogResult(status, result, errorData, error); } } } ================================================ FILE: Assets/AppsFlyer/AFSDKValidateAndLogResult.cs.meta ================================================ fileFormatVersion: 2 guid: 2df1c6f1eab2e4849bf2762a8d78933f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyer.asmdef ================================================ { "name": "AppsFlyer", "references": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/AppsFlyer/AppsFlyer.asmdef.meta ================================================ fileFormatVersion: 2 guid: 2a37df438292d4903b4e5159c5de3bf9 AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyer.cs ================================================ using System; using System.Collections.Generic; using UnityEngine; namespace AppsFlyerSDK { public class AppsFlyer : MonoBehaviour { public static readonly string kAppsFlyerPluginVersion = "6.17.91"; public static string CallBackObjectName = null; private static EventHandler onRequestResponse; private static EventHandler onInAppResponse; private static EventHandler onDeepLinkReceived; public static IAppsFlyerNativeBridge instance = null; public delegate void unityCallBack(string message); /// /// Initialize the AppsFlyer SDK with your devKey and appID. /// The dev key is required on all platforms, and the appID is required for iOS. /// If you app is for Android only pass null for the appID. /// /// AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. /// Your app's Apple ID. /// /// /// AppsFlyer.initSDK("K2***********99", "41*****44""); /// /// public static void initSDK(string devKey, string appID) { initSDK(devKey, appID, null); } /// /// Initialize the AppsFlyer SDK with your devKey and appID. /// The dev key is required on all platforms, and the appID is required for iOS. /// If you app is for Android only pass null for the appID. /// /// AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. /// Your app's Apple ID. /// pass the script of the game object being used. /// /// /// AppsFlyer.initSDK("K2***********99", 41*****44, this); /// /// public static void initSDK(string devKey, string appID, MonoBehaviour gameObject) { if (gameObject != null) { #if UNITY_STANDALONE_OSX CallBackObjectName = gameObject.GetType().ToString(); #else CallBackObjectName = gameObject.name; #endif } #if UNITY_IOS || UNITY_STANDALONE_OSX if (instance == null || !instance.isInit) { instance = new AppsFlyeriOS(devKey, appID, gameObject); instance.isInit = true; } #elif UNITY_ANDROID if (instance == null || !instance.isInit) { AppsFlyerAndroid appsFlyerAndroid = new AppsFlyerAndroid(); appsFlyerAndroid.initSDK(devKey, gameObject); instance = appsFlyerAndroid; instance.isInit = true; } #elif UNITY_WSA_10_0 AppsFlyerWindows.InitSDK(devKey, appID, gameObject); if (gameObject != null) { AppsFlyerWindows.GetConversionData(gameObject.name); } #else #endif } /// /// Once this API is invoked, our SDK will start. /// Once the API is called a sessions will be immediately sent, and all background forground transitions will send a session. /// public static void startSDK() { #if UNITY_WSA_10_0 AppsFlyerWindows.Start(); #else if (instance != null) { instance.startSDK(onRequestResponse != null, CallBackObjectName); } #endif } /// /// Send an In-App Event. /// In-App Events provide insight on what is happening in your app. /// /// Event Name as String. /// Event Values as Dictionary. public static void sendEvent(string eventName, Dictionary eventValues) { #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyerWindows.LogEvent(eventName, eventValues); #else if (instance != null) { instance.sendEvent(eventName, eventValues, onInAppResponse != null, CallBackObjectName); } #endif } /// /// Once this API is invoked, our SDK no longer communicates with our servers and stops functioning. /// In some extreme cases you might want to shut down all SDK activity due to legal and privacy compliance. /// This can be achieved with the stopSDK API. /// /// should sdk be stopped. public static void stopSDK(bool isSDKStopped) { if (instance != null) { instance.stopSDK(isSDKStopped); } } // /// Was the stopSDK(boolean) API set to true. /// /// boolean isSDKStopped. public static bool isSDKStopped() { if (instance != null) { return instance.isSDKStopped(); } return false; } /// /// Get the AppsFlyer SDK version used in app. /// /// The current SDK version. public static string getSdkVersion() { if (instance != null) { return instance.getSdkVersion(); } return ""; } /// /// Enables Debug logs for the AppsFlyer SDK. /// Should only be set to true in development / debug. /// /// shouldEnable boolean. public static void setIsDebug(bool shouldEnable) { if (instance != null) { instance.setIsDebug(shouldEnable); } else { #if UNITY_IOS || UNITY_STANDALONE_OSX instance = new AppsFlyeriOS(); instance.setIsDebug(shouldEnable); #elif UNITY_ANDROID instance = new AppsFlyerAndroid(); instance.setIsDebug(shouldEnable); #else #endif } } /// /// Setting your own customer ID enables you to cross-reference your own unique ID with AppsFlyer’s unique ID and the other devices’ IDs. /// This ID is available in AppsFlyer CSV reports along with Postback APIs for cross-referencing with your internal IDs. /// /// Customer ID for client. public static void setCustomerUserId(string id) { #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyerWindows.SetCustomerUserId(id); #else if (instance != null) { instance.setCustomerUserId(id); } #endif } /// /// Set the OneLink ID that should be used for User-Invite-API. /// The link that is generated for the user invite will use this OneLink as the base link. /// /// OneLink ID obtained from the AppsFlyer Dashboard. public static void setAppInviteOneLinkID(string oneLinkId) { if (instance != null) { instance.setAppInviteOneLinkID(oneLinkId); } } /// /// Set the deepLink timeout value that should be used for DDL. /// /// deepLink timeout in milliseconds. public static void setDeepLinkTimeout(long deepLinkTimeout) { if (instance != null) { instance.setDeepLinkTimeout(deepLinkTimeout); } } /// /// Set additional data to be sent to AppsFlyer. /// /// additional data Dictionary. public static void setAdditionalData(Dictionary customData) { if (instance != null) { instance.setAdditionalData(customData); } } /// /// Advertisers can wrap AppsFlyer OneLink within another Universal Link. /// This Universal Link will invoke the app but any deep linking data will not propagate to AppsFlyer. /// /// Array of urls. public static void setResolveDeepLinkURLs(params string[] urls) { if (instance != null) { instance.setResolveDeepLinkURLs(urls); } } /// /// Advertisers can use this method to set vanity onelink domains. /// /// Array of domains. public static void setOneLinkCustomDomain(params string[] domains) { if (instance != null) { instance.setOneLinkCustomDomain(domains); } else { #if UNITY_IOS || UNITY_STANDALONE_OSX instance = new AppsFlyeriOS(); #elif UNITY_ANDROID instance = new AppsFlyerAndroid(); #else #endif } } /// /// Setting user local currency code for in-app purchases. /// The currency code should be a 3 character ISO 4217 code. (default is USD). /// You can set the currency code for all events by calling the following method. /// /// 3 character ISO 4217 code. public static void setCurrencyCode(string currencyCode) { if (instance != null) { instance.setCurrencyCode(currencyCode); } else { #if UNITY_IOS || UNITY_STANDALONE_OSX instance = new AppsFlyeriOS(); instance.setCurrencyCode(currencyCode); #elif UNITY_ANDROID instance = new AppsFlyerAndroid(); instance.setCurrencyCode(currencyCode); #else #endif } } /// /// Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage purposes within the application. /// /// instance of AppsFlyerConsent. public static void setConsentData(AppsFlyerConsent appsFlyerConsent) { if (instance != null) { instance.setConsentData(appsFlyerConsent); } } /// /// Logs ad revenue data along with additional parameters if provided. /// /// instance of AFAdRevenueData containing ad revenue information. /// An optional map of additional parameters to be logged with ad revenue data. This can be null if there are no additional parameters. public static void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary additionalParameters) { if (instance != null) { instance.logAdRevenue(adRevenueData, additionalParameters); } } /// /// Manually record the location of the user. /// /// latitude as double. /// longitude as double. public static void recordLocation(double latitude, double longitude) { if (instance != null) { instance.recordLocation(latitude, longitude); } } /// /// Anonymize user Data. /// Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions. /// Default is false. /// /// shouldAnonymizeUser boolean. public static void anonymizeUser(bool shouldAnonymizeUser) { if (instance != null) { instance.anonymizeUser(shouldAnonymizeUser); } } /// /// Calling enableTCFDataCollection(true) will enable collecting and sending any TCF related data. /// Calling enableTCFDataCollection(false) will disable the collection of TCF related data and from sending it. /// /// should start TCF Data collection boolean. public static void enableTCFDataCollection(bool shouldCollectTcfData) { if (instance != null) { instance.enableTCFDataCollection(shouldCollectTcfData); } } /// /// Get AppsFlyer's unique device ID which is created for every new install of an app. /// /// AppsFlyer's unique device ID. public static string getAppsFlyerId() { #if UNITY_WSA_10_0 && !UNITY_EDITOR return AppsFlyerWindows.GetAppsFlyerId(); #else if (instance != null) { return instance.getAppsFlyerId(); } #endif return string.Empty; } /// /// Set a custom value for the minimum required time between sessions. /// By default, at least 5 seconds must lapse between 2 app launches to count as separate 2 sessions. /// /// minimum time between 2 separate sessions in seconds. public static void setMinTimeBetweenSessions(int seconds) { if (instance != null) { instance.setMinTimeBetweenSessions(seconds); } } /// /// Set a custom host. /// /// Host prefix. /// Host name. public static void setHost(string hostPrefixName, string hostName) { if (instance != null) { instance.setHost(hostPrefixName, hostName); } else { #if UNITY_IOS || UNITY_STANDALONE_OSX instance = new AppsFlyeriOS(); instance.setHost(hostPrefixName, hostName); #elif UNITY_ANDROID instance = new AppsFlyerAndroid(); instance.setHost(hostPrefixName, hostName); #else #endif } } /// /// Set the user emails and encrypt them. /// cryptMethod Encryption method: /// EmailCryptType.EmailCryptTypeMD5 /// EmailCryptType.EmailCryptTypeSHA1 /// EmailCryptType.EmailCryptTypeSHA256 /// EmailCryptType.EmailCryptTypeNone /// /// Encryption method. /// User emails. public static void setUserEmails(EmailCryptType cryptType, params string[] userEmails) { if (instance != null) { instance.setUserEmails(cryptType, userEmails); } } public static void updateServerUninstallToken(string token) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.updateServerUninstallToken(token); } } /// /// Set the user phone number. /// /// phoneNumber string public static void setPhoneNumber(string phoneNumber) { if (instance != null) { instance.setPhoneNumber(phoneNumber); } } public static void setImeiData(string aImei) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setImeiData(aImei); } } /// /// Used by advertisers to exclude all networks/integrated partners from getting data. /// [Obsolete("Please use setSharingFilterForPartners api")] public static void setSharingFilterForAllPartners() { if (instance != null) { instance.setSharingFilterForAllPartners(); } } public static void setAndroidIdData(string aAndroidId) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setAndroidIdData(aAndroidId); } } public static void waitForCustomerUserId(bool wait) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.waitForCustomerUserId(wait); } } /// /// Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data. /// /// partners to exclude from getting data [Obsolete("Please use setSharingFilterForPartners api")] public static void setSharingFilter(params string[] partners) { if (instance != null) { instance.setSharingFilter(partners); } } public static void setCustomerIdAndStartSDK(string id) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setCustomerIdAndStartSDK(id); } } /// /// Lets you configure how which partners should the SDK exclude from data-sharing. /// /// partners to exclude from getting data public static void setSharingFilterForPartners(params string[] partners) { #if UNITY_IOS || UNITY_STANDALONE_OSX AppsFlyeriOS.setSharingFilterForPartners(partners); #elif UNITY_ANDROID AppsFlyerAndroid.setSharingFilterForPartners(partners); #else #endif } public static string getOutOfStore() { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; return appsFlyerAndroidInstance.getOutOfStore(); } return string.Empty; } public static void setOutOfStore(string sourceName) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setOutOfStore(sourceName); } } /// /// Register a Conversion Data Listener. /// Allows the developer to access the user attribution data in real-time for every new install, directly from the SDK level. /// By doing this you can serve users with personalized content or send them to specific activities within the app, /// which can greatly enhance their engagement with your app. /// /// /// /// AppsFlyer.getConversionData(this.name); /// /// public static void getConversionData(string objectName) { #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyerWindows.GetConversionData(""); #else if (instance != null) { instance.getConversionData(objectName); } #endif } public static void setCollectAndroidID(bool isCollect) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setCollectAndroidID(isCollect); } } public static void setIsUpdate(bool isUpdate) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setIsUpdate(isUpdate); } } public static void setCollectIMEI(bool isCollect) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setCollectIMEI(isCollect); } } public static void setDisableCollectAppleAdSupport(bool disable) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setDisableCollectAppleAdSupport(disable); } } public static void setShouldCollectDeviceName(bool shouldCollectDeviceName) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setShouldCollectDeviceName(shouldCollectDeviceName); } } /// /// Use the following API to attribute the click and launch the app store's app page. /// /// promoted App ID /// cross promotion campaign /// additional user params /// /// /// Dictionary parameters = new Dictionary(); /// parameters.Add("af_sub1", "val"); /// parameters.Add("custom_param", "val2"); /// AppsFlyer.attributeAndOpenStore("123456789", "test campaign", parameters, this); /// /// public static void attributeAndOpenStore(string appID, string campaign, Dictionary userParams, MonoBehaviour gameObject) { if (instance != null) { instance.attributeAndOpenStore(appID, campaign, userParams, gameObject); } } public static void setPreinstallAttribution(string mediaSource, string campaign, string siteId) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setPreinstallAttribution(mediaSource, campaign, siteId); } } public static void setDisableCollectIAd(bool disableCollectIAd) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setDisableCollectIAd(disableCollectIAd); } } public static bool isPreInstalledApp() { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; return appsFlyerAndroidInstance.isPreInstalledApp(); } return false; } public static void setUseReceiptValidationSandbox(bool useReceiptValidationSandbox) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setUseReceiptValidationSandbox(useReceiptValidationSandbox); } } /// /// To attribute an impression use the following API call. /// Make sure to use the promoted App ID as it appears within the AppsFlyer dashboard. /// /// promoted App ID. /// cross promotion campaign. /// parameters Dictionary. public static void recordCrossPromoteImpression(string appID, string campaign, Dictionary parameters) { if (instance != null) { instance.recordCrossPromoteImpression(appID, campaign, parameters); } } public static void setUseUninstallSandbox(bool useUninstallSandbox) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setUseUninstallSandbox(useUninstallSandbox); } } public static string getAttributionId() { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; return appsFlyerAndroidInstance.getAttributionId(); } return string.Empty; } public static void handlePushNotifications() { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.handlePushNotifications(); } } /// /// [Deprecated] Validates an in-app purchase on iOS. /// Use the V2 overload with AFSDKPurchaseDetailsIOS instead. /// [System.Obsolete("This method is deprecated. Use validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) instead.")] public static void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, Dictionary additionalParameters, MonoBehaviour gameObject) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.validateAndSendInAppPurchase(productIdentifier, price, currency, transactionId, additionalParameters, gameObject); } } /// /// Validates an in-app purchase on iOS using the V2 API. /// public static void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.validateAndSendInAppPurchase(details, purchaseAdditionalDetails, gameObject); } } /// /// [Deprecated] Validates an in-app purchase on Android. /// Use the V2 overload with AFPurchaseDetailsAndroid instead. /// [System.Obsolete("This method is deprecated. Use validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) instead.")] public static void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary additionalParameters, MonoBehaviour gameObject) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.validateAndSendInAppPurchase(publicKey, signature,purchaseData, price, currency, additionalParameters, gameObject); } } /// /// Validates an in-app purchase on Android using the V2 API. /// public static void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.validateAndSendInAppPurchase(details, purchaseAdditionalDetails, gameObject); } } public static void handleOpenUrl(string url, string sourceApplication, string annotation) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.handleOpenUrl(url, sourceApplication, annotation); } } public static void registerUninstall(byte[] deviceToken) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.registerUninstall(deviceToken); } } public static void waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.waitForATTUserAuthorizationWithTimeoutInterval(timeoutInterval); } } public static void setCurrentDeviceLanguage(string language) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.setCurrentDeviceLanguage(language); } } /// /// The LinkGenerator class builds the invite URL according to various setter methods which allow passing on additional information on the click. /// See - https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution- /// /// parameters Dictionary. public static void generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject) { if (instance != null) { instance.generateUserInviteLink(parameters, gameObject); } } public static void disableSKAdNetwork(bool isDisabled) { if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.disableSKAdNetwork(isDisabled); } else { #if UNITY_IOS || UNITY_STANDALONE_OSX instance = new AppsFlyeriOS(); IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.disableSKAdNetwork(isDisabled); #else #endif } } public static void setCollectOaid(bool isCollect) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setCollectOaid(isCollect); } } /// /// Use this method if you’re integrating your app with push providers /// that don’t use the default push notification JSON schema the SDK expects. /// See docs for more info. /// /// array of nested json path public static void addPushNotificationDeepLinkPath(params string[] paths) { if (instance != null) { instance.addPushNotificationDeepLinkPath(paths); } } public static void setDisableAdvertisingIdentifiers(bool disable) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setDisableAdvertisingIdentifiers(disable); } } /// /// Subscribe for unified deeplink API. /// This is called automatically from OnDeepLinkReceived. /// CallBackObjectName is set in the init method. /// public static void subscribeForDeepLink() { if (instance != null) { instance.subscribeForDeepLink(CallBackObjectName); } } /// /// Allows sending custom data for partner integration purposes. /// partnerId : id of the partner /// partnerInfo: customer data /// public static void setPartnerData(string partnerId, Dictionary partnerInfo) { if (instance != null) { instance.setPartnerData(partnerId, partnerInfo); } } /// /// Use to opt-out of collecting the network operator name (carrier) and sim operator name from the device. /// public static void setDisableNetworkData(bool disable) { if (instance != null && instance is IAppsFlyerAndroidBridge) { IAppsFlyerAndroidBridge appsFlyerAndroidInstance = (IAppsFlyerAndroidBridge)instance; appsFlyerAndroidInstance.setDisableNetworkData(disable); } } /// /// Use to disable app vendor identifier (IDFV) collection, 'true' to disable. /// public static void disableIDFVCollection(bool isDisabled) { #if UNITY_IOS || UNITY_STANDALONE_OSX if (instance == null) { instance = new AppsFlyeriOS(); } if (instance != null && instance is IAppsFlyerIOSBridge) { IAppsFlyerIOSBridge appsFlyeriOSInstance = (IAppsFlyerIOSBridge)instance; appsFlyeriOSInstance.disableIDFVCollection(isDisabled); } #else #endif } /// /// Start callback event. /// public static event EventHandler OnRequestResponse { add { onRequestResponse += value; } remove { onRequestResponse -= value; } } /// /// In-App callback event. /// public static event EventHandler OnInAppResponse { add { onInAppResponse += value; } remove { onInAppResponse -= value; } } /// /// Unified DeepLink Event /// public static event EventHandler OnDeepLinkReceived { add { onDeepLinkReceived += value; subscribeForDeepLink(); } remove { onDeepLinkReceived -= value; } } /// /// Used to accept start callback from UnitySendMessage on native side. /// public void inAppResponseReceived(string response) { if (onInAppResponse != null) { onInAppResponse.Invoke(null, parseRequestCallback(response)); } } /// /// Used to accept in-app callback from UnitySendMessage on native side. /// public void requestResponseReceived(string response) { if (onRequestResponse != null) { onRequestResponse.Invoke(null, parseRequestCallback(response)); } } /// /// Used to accept deeplink callback from UnitySendMessage on native side. /// public void onDeepLinking(string response) { DeepLinkEventsArgs args = new DeepLinkEventsArgs(response); if (onDeepLinkReceived != null) { onDeepLinkReceived.Invoke(null, args); } } private static AppsFlyerRequestEventArgs parseRequestCallback(string response) { int responseCode = 0; string errorDescription = ""; try { Dictionary dictionary = CallbackStringToDictionary(response); var errorResponse = dictionary.ContainsKey("errorDescription") ? dictionary["errorDescription"] : ""; errorDescription = (string)errorResponse; responseCode = (int)(long) dictionary["statusCode"]; } catch (Exception e) { AFLog("parseRequestCallback", String.Format("{0} Exception caught.", e)); } return new AppsFlyerRequestEventArgs(responseCode, errorDescription); } /// /// Helper method to convert json strings to dictionary. /// /// json string /// dictionary representing the input json string. public static Dictionary CallbackStringToDictionary(string str) { return AFMiniJSON.Json.Deserialize(str) as Dictionary; } /// /// Helper method to log AppsFlyer events and callbacks. /// /// method name /// message to log public static void AFLog(string methodName, string str) { Debug.Log(string.Format("AppsFlyer_Unity_v{0} {1} called with {2}", kAppsFlyerPluginVersion, methodName, str)); } } public enum EmailCryptType { // None EmailCryptTypeNone = 0, // SHA256 EmailCryptTypeSHA256 = 1, } } ================================================ FILE: Assets/AppsFlyer/AppsFlyer.cs.meta ================================================ fileFormatVersion: 2 guid: 45161025a517d427381d3d06153a5ad3 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerAndroid.cs ================================================ using System; using System.Collections.Generic; using UnityEngine; namespace AppsFlyerSDK { #if UNITY_ANDROID public class AppsFlyerAndroid : IAppsFlyerAndroidBridge { public bool isInit { get; set; } private static AndroidJavaClass appsFlyerAndroid = new AndroidJavaClass("com.appsflyer.unity.AppsFlyerAndroidWrapper"); public AppsFlyerAndroid() { } /// /// Use this method to init the sdk for the application. /// Call this method before startSDK. /// /// AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. /// The current game object. This is used to get the conversion data callbacks. Pass null if you do not need the callbacks. public void initSDK(string devkey, MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("initSDK", devkey, gameObject ? gameObject.name : null); #endif } /// /// Use this method to start the sdk for the application. /// The AppsFlyer's Dev-Key must be provided. /// /// AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. public void startSDK(bool onRequestResponse, string CallBackObjectName) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("startTracking", onRequestResponse, CallBackObjectName); #endif } /// /// Once this API is invoked, our SDK no longer communicates with our servers and stops functioning. /// In some extreme cases you might want to shut down all SDK activity due to legal and privacy compliance. /// This can be achieved with the stopSDK API. /// /// boolean should SDK be stopped. public void stopSDK(bool isSDKStopped) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("stopTracking", isSDKStopped); #endif } /// /// Get the AppsFlyer SDK version used in app. /// /// AppsFlyer SDK version. public string getSdkVersion() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getSdkVersion"); #else return ""; #endif } /// /// Manually pass the Firebase / GCM Device Token for Uninstall measurement. /// /// Firebase Device Token. public void updateServerUninstallToken(string token) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("updateServerUninstallToken", token); #endif } /// /// Enables Debug logs for the AppsFlyer SDK. /// Should only be set to true in development / debug. /// /// shouldEnable boolean. public void setIsDebug(bool shouldEnable) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setIsDebug", shouldEnable); #endif } /// /// By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat (4.4) /// and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS). /// Use this API to explicitly send IMEI to AppsFlyer. /// /// device's IMEI. public void setImeiData(string aImei) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setImeiData", aImei); #endif } /// /// By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat(4.4) /// and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS). /// Use this API to explicitly send Android ID to AppsFlyer. /// /// device's Android ID. public void setAndroidIdData(string aAndroidId) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setAndroidIdData", aAndroidId); #endif } /// /// Setting your own customer ID enables you to cross-reference your own unique ID with AppsFlyer’s unique ID and the other devices’ IDs. /// This ID is available in AppsFlyer CSV reports along with Postback APIs for cross-referencing with your internal IDs. /// /// Customer ID for client. public void setCustomerUserId(string id) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCustomerUserId", id); #endif } /// /// It is possible to delay the SDK Initialization until the customerUserID is set. /// This feature makes sure that the SDK doesn't begin functioning until the customerUserID is provided. /// If this API is used, all in-app events and any other SDK API calls are discarded, until the customerUserID is provided. /// /// wait boolean. public void waitForCustomerUserId(bool wait) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("waitForCustomerUserId", wait); #endif } /// /// Use this API to provide the SDK with the relevant customer user id and trigger the SDK to begin its normal activity. /// /// Customer ID for client. public void setCustomerIdAndStartSDK(string id) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCustomerIdAndTrack", id); #endif } /// /// Get the current AF_STORE value. /// /// AF_Store value. public string getOutOfStore() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getOutOfStore"); #else return ""; #endif } /// /// Manually set the AF_STORE value. /// /// value to be set. public void setOutOfStore(string sourceName) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setOutOfStore", sourceName); #endif } /// /// Set the OneLink ID that should be used for User-Invites. /// The link that is generated for the user invite will use this OneLink as the base link. /// /// OneLink ID obtained from the AppsFlyer Dashboard. public void setAppInviteOneLinkID(string oneLinkId) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setAppInviteOneLinkID", oneLinkId); #endif } /// /// Set additional data to be sent to AppsFlyer. /// /// additional data Dictionary. public void setAdditionalData(Dictionary customData) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setAdditionalData", convertDictionaryToJavaMap(customData)); #endif } //// /// Set the deepLink timeout value that should be used for DDL. /// /// deepLink timeout in milliseconds. public void setDeepLinkTimeout(long deepLinkTimeout) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setDeepLinkTimeout", deepLinkTimeout); #endif } /// /// Set the user emails. /// /// User emails. public void setUserEmails(params string[] userEmails) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setUserEmails", (object)userEmails); #endif } /// /// Set the user phone number. /// /// User phoneNumber. public void setPhoneNumber(string phoneNumber){ #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setPhoneNumber", phoneNumber); #endif } /// /// Set the user emails and encrypt them. /// cryptMethod Encryption method: /// EmailCryptType.EmailCryptTypeMD5 /// EmailCryptType.EmailCryptTypeSHA1 /// EmailCryptType.EmailCryptTypeSHA256 /// EmailCryptType.EmailCryptTypeNone /// /// Encryption method. /// User emails. public void setUserEmails(EmailCryptType cryptMethod, params string[] emails) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setUserEmails", getEmailType(cryptMethod), (object)emails); #endif } /// /// Opt-out of collection of Android ID. /// If the app does NOT contain Google Play Services, Android ID is collected by the SDK. /// However, apps with Google play services should avoid Android ID collection as this is in violation of the Google Play policy. /// /// boolean, false to opt-out. public void setCollectAndroidID(bool isCollect) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCollectAndroidID", isCollect); #endif } /// /// Opt-out of collection of IMEI. /// If the app does NOT contain Google Play Services, device IMEI is collected by the SDK. /// However, apps with Google play services should avoid IMEI collection as this is in violation of the Google Play policy. /// /// boolean, false to opt-out. public void setCollectIMEI(bool isCollect) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCollectIMEI", isCollect); #endif } /// /// Advertisers can wrap AppsFlyer OneLink within another Universal Link. /// This Universal Link will invoke the app but any deep linking data will not propagate to AppsFlyer. /// /// Array of urls. public void setResolveDeepLinkURLs(params string[] urls) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setResolveDeepLinkURLs", (object)urls); #endif } /// /// Advertisers can use this method to set vanity onelink domains. /// /// Array of domains. public void setOneLinkCustomDomain(params string[] domains) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setOneLinkCustomDomain", (object)domains); #endif } /// /// Manually set that the application was updated. /// /// isUpdate boolean value. public void setIsUpdate(bool isUpdate) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setIsUpdate", isUpdate); #endif } /// /// Setting user local currency code for in-app purchases. /// The currency code should be a 3 character ISO 4217 code. (default is USD). /// You can set the currency code for all events by calling the following method. /// /// 3 character ISO 4217 code. public void setCurrencyCode(string currencyCode) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCurrencyCode", currencyCode); #endif } /// /// Manually record the location of the user. /// /// latitude as double. /// longitude as double. public void recordLocation(double latitude, double longitude) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("trackLocation", latitude, longitude); #endif } /// /// Send an In-App Event. /// In-App Events provide insight on what is happening in your app. /// /// Event Name as String. /// Event Values as Dictionary. public void sendEvent(string eventName, Dictionary eventValues) { sendEvent(eventName, eventValues, false, AppsFlyer.CallBackObjectName); } public void sendEvent(string eventName, Dictionary eventValues, bool shouldCallback, string callBackObjectName) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("trackEvent", eventName, convertDictionaryToJavaMap(eventValues), shouldCallback, callBackObjectName); #endif } /// /// Anonymize user Data. /// Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions. /// Default is false. /// /// isDisabled boolean. public void anonymizeUser(bool isDisabled) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setDeviceTrackingDisabled", isDisabled); #endif } /// /// Calling enableTCFDataCollection(true) will enable collecting and sending any TCF related data. /// Calling enableTCFDataCollection(false) will disable the collection of TCF related data and from sending it. /// /// should start TCF Data collection boolean. public void enableTCFDataCollection(bool shouldCollectTcfData) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("enableTCFDataCollection", shouldCollectTcfData); #endif } /// /// Enable the collection of Facebook Deferred AppLinks. /// Requires Facebook SDK and Facebook app on target/client device. /// This API must be invoked prior to initializing the AppsFlyer SDK in order to function properly. /// /// should Facebook's deferred app links be processed by the AppsFlyer SDK. public void enableFacebookDeferredApplinks(bool isEnabled) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("enableFacebookDeferredApplinks", isEnabled); #endif } /// /// Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage purposes within the application. /// call this method when GDPR user is true /// /// hasConsentForDataUsage boolean. /// hasConsentForAdsPersonalization boolean. public void setConsentData(AppsFlyerConsent appsFlyerConsent) { #if !UNITY_EDITOR string isUserSubjectToGDPR = appsFlyerConsent.isUserSubjectToGDPR?.ToString().ToLower() ?? "null"; string hasConsentForDataUsage = appsFlyerConsent.hasConsentForDataUsage?.ToString().ToLower() ?? "null"; string hasConsentForAdsPersonalization = appsFlyerConsent.hasConsentForAdsPersonalization?.ToString().ToLower() ?? "null"; string hasConsentForAdStorage = appsFlyerConsent.hasConsentForAdStorage?.ToString().ToLower() ?? "null"; appsFlyerAndroid.CallStatic("setConsentData", isUserSubjectToGDPR, hasConsentForDataUsage, hasConsentForAdsPersonalization, hasConsentForAdStorage); #endif } /// /// Logs ad revenue data along with additional parameters if provided. /// instance of AFAdRevenueData containing ad revenue information. /// An optional map of additional parameters to be logged with ad revenue data. This can be null if there are no additional parameters. public void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary additionalParameters) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("logAdRevenue", adRevenueData.monetizationNetwork, getMediationNetwork(adRevenueData.mediationNetwork), adRevenueData.currencyIso4217Code, adRevenueData.eventRevenue, convertDictionaryToJavaMap(additionalParameters)); #endif } /// /// Restrict reengagement via deep-link to once per each unique deep-link. /// Otherwise deep re-occurring deep-links will be permitted for non-singleTask Activities and deep-linking via AppsFlyer deep-links. /// The default value is false. /// /// doConsume boolean. public void setConsumeAFDeepLinks(bool doConsume) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setConsumeAFDeepLinks", doConsume); #endif } /// /// Specify the manufacturer or media source name to which the preinstall is attributed. /// /// Manufacturer or media source name for preinstall attribution. /// Campaign name for preinstall attribution. /// Site ID for preinstall attribution. public void setPreinstallAttribution(string mediaSource, string campaign, string siteId) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setPreinstallAttribution", mediaSource, campaign, siteId); #endif } /// /// Boolean indicator for preinstall by Manufacturer. /// /// boolean isPreInstalledApp. public bool isPreInstalledApp() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("isPreInstalledApp"); #else return false; #endif } /// /// Get the Facebook attribution ID, if one exists. /// /// string Facebook attribution ID. public string getAttributionId() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getAttributionId"); #else return ""; #endif } /// /// Get AppsFlyer's unique device ID is created for every new install of an app. /// /// AppsFlyer's unique device ID. public string getAppsFlyerId() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getAppsFlyerId"); #else return ""; #endif } /// /// [Deprecated] API for server verification of in-app purchases - please use V2 with AFPurchaseDetailsAndroid instead. /// An af_purchase event with the relevant values will be automatically sent if the validation is successful. /// /// License Key obtained from the Google Play Console. /// data.INAPP_DATA_SIGNATURE from onActivityResult(int requestCode, int resultCode, Intent data) /// data.INAPP_PURCHASE_DATA from onActivityResult(int requestCode, int resultCode, Intent data) /// Purchase price, should be derived from skuDetails.getStringArrayList("DETAILS_LIST") /// Purchase currency, should be derived from skuDetails.getStringArrayList("DETAILS_LIST") /// additionalParameters Freehand parameters to be sent with the purchase (if validated). [System.Obsolete("This method is deprecated. Use validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) instead.")] public void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary additionalParameters, MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("validateAndTrackInAppPurchase", publicKey, signature, purchaseData, price, currency, convertDictionaryToJavaMap(additionalParameters), gameObject ? gameObject.name : null); #endif } /// /// V2 - API for server verification of in-app purchases. /// An af_purchase event with the relevant values will be automatically sent if the validation is successful. /// /// AFPurchaseDetailsAndroid instance. /// purchaseAdditionalDetails Freehand parameters to be sent with the purchase (if validated). public void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("validateAndTrackInAppPurchaseV2", (int)details.purchaseType, details.purchaseToken, details.productId, convertDictionaryToJavaMap(purchaseAdditionalDetails), gameObject ? gameObject.name : null); #endif } /// /// Was the stopSDK(boolean) API set to true. /// /// boolean isSDKStopped. public bool isSDKStopped() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("isTrackingStopped"); #else return false; #endif } /// /// Set a custom value for the minimum required time between sessions. /// By default, at least 5 seconds must lapse between 2 app launches to count as separate 2 sessions. /// /// minimum time between 2 separate sessions in seconds. public void setMinTimeBetweenSessions(int seconds) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setMinTimeBetweenSessions", seconds); #endif } /// /// Set a custom host. /// /// Host prefix. /// Host name. public void setHost(string hostPrefixName, string hostName) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setHost", hostPrefixName, hostName); #endif } /// /// Get the host name. /// Default value is "appsflyer.com". /// /// Host name. public string getHostName() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getHostName"); #else return ""; #endif } /// /// Get the custom host prefix. /// /// Host prefix. public string getHostPrefix() { #if !UNITY_EDITOR return appsFlyerAndroid.CallStatic("getHostPrefix"); #else return ""; #endif } /// /// Used by advertisers to exclude all networks/integrated partners from getting data. /// public void setSharingFilterForAllPartners() { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setSharingFilterForAllPartners"); #endif } /// /// Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data. /// /// partners to exclude from getting data public void setSharingFilter(params string[] partners) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setSharingFilter", (object)partners); #endif } /// /// Lets you configure how which partners should the SDK exclude from data-sharing. /// /// partners to exclude from getting data public static void setSharingFilterForPartners(params string[] partners) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setSharingFilterForPartners", (object)partners); #endif } /// /// Register a Conversion Data Listener. /// Allows the developer to access the user attribution data in real-time for every new install, directly from the SDK level. /// By doing this you can serve users with personalized content or send them to specific activities within the app, /// which can greatly enhance their engagement with your app. /// public void getConversionData(string objectName) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("getConversionData", objectName); #endif } /// /// Register a validation listener for the validateAndSendInAppPurchase API. /// public void initInAppPurchaseValidatorListener(MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("initInAppPurchaseValidatorListener", gameObject ? gameObject.name : null); #endif } /// /// setCollectOaid /// You must include the appsflyer oaid library for this api to work. /// /// isCollect oaid - set fasle to opt out public void setCollectOaid(bool isCollect) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setCollectOaid", isCollect); #endif } /// /// Use the following API to attribute the click and launch the app store's app page. /// /// promoted App ID /// cross promotion campaign /// additional user params public void attributeAndOpenStore(string promoted_app_id, string campaign, Dictionary userParams, MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("attributeAndOpenStore", promoted_app_id, campaign, convertDictionaryToJavaMap(userParams)); #endif } /// /// To attribute an impression use the following API call. /// Make sure to use the promoted App ID as it appears within the AppsFlyer dashboard. /// /// promoted App ID. /// cross promotion campaign. /// parameters Dictionary. public void recordCrossPromoteImpression(string appID, string campaign, Dictionary parameters) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("recordCrossPromoteImpression", appID, campaign, convertDictionaryToJavaMap(parameters)); #endif } /// /// The LinkGenerator class builds the invite URL according to various setter methods which allow passing on additional information on the click. /// See - https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution- /// /// parameters Dictionary. public void generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("createOneLinkInviteListener", convertDictionaryToJavaMap(parameters), gameObject ? gameObject.name : null); #endif } /// /// To measure push notifications as part of a retargeting campaign. /// public void handlePushNotifications(){ #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("handlePushNotifications"); #endif } /// /// Use this method if you’re integrating your app with push providers /// that don’t use the default push notification JSON schema the SDK expects. /// See docs for more info. /// /// array of nested json path public void addPushNotificationDeepLinkPath(params string[] paths) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("addPushNotificationDeepLinkPath", (object)paths); #endif } /// /// subscribe to unified deep link callbacks /// public void subscribeForDeepLink(string objectName){ #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("subscribeForDeepLink", objectName); #endif } /// /// Disables collection of various Advertising IDs by the SDK. This includes Google Advertising ID (GAID), OAID and Amazon Advertising ID (AAID) /// /// disable boolean. public void setDisableAdvertisingIdentifiers(bool disable) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setDisableAdvertisingIdentifiers", disable); #endif } /// /// Allows sending custom data for partner integration purposes. /// public void setPartnerData(string partnerId, Dictionary partnerInfo) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setPartnerData", partnerId, convertDictionaryToJavaMap(partnerInfo)); #endif } /// /// Use to opt-out of collecting the network operator name (carrier) and sim operator name from the device. /// public void setDisableNetworkData(bool disable) { #if !UNITY_EDITOR appsFlyerAndroid.CallStatic("setDisableNetworkData", disable); #endif } /// /// Internal Helper Method. /// private static AndroidJavaObject getEmailType(EmailCryptType cryptType) { AndroidJavaClass emailsCryptTypeEnum = new AndroidJavaClass("com.appsflyer.AppsFlyerProperties$EmailsCryptType"); AndroidJavaObject emailsCryptType; switch (cryptType) { case EmailCryptType.EmailCryptTypeSHA256: emailsCryptType = emailsCryptTypeEnum.GetStatic("SHA256"); break; default: emailsCryptType = emailsCryptTypeEnum.GetStatic("NONE"); break; } return emailsCryptType; } /// /// Internal Helper Method. /// private static AndroidJavaObject getMediationNetwork(MediationNetwork mediationNetwork) { AndroidJavaClass mediationNetworkEnumClass = new AndroidJavaClass("com.appsflyer.MediationNetwork"); AndroidJavaObject mediationNetworkObject; switch (mediationNetwork) { case MediationNetwork.IronSource: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("IRONSOURCE"); break; case MediationNetwork.ApplovinMax: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("APPLOVIN_MAX"); break; case MediationNetwork.GoogleAdMob: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("GOOGLE_ADMOB"); break; case MediationNetwork.Fyber: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("FYBER"); break; case MediationNetwork.Appodeal: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("APPODEAL"); break; case MediationNetwork.Admost: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("ADMOST"); break; case MediationNetwork.Topon: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("TOPON"); break; case MediationNetwork.Tradplus: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("TRADPLUS"); break; case MediationNetwork.Yandex: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("YANDEX"); break; case MediationNetwork.ChartBoost: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("CHARTBOOST"); break; case MediationNetwork.Unity: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("UNITY"); break; case MediationNetwork.ToponPte: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("TOPON_PTE"); break; case MediationNetwork.Custom: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("CUSTOM_MEDIATION"); break; case MediationNetwork.DirectMonetization: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("DIRECT_MONETIZATION_NETWORK"); break; default: mediationNetworkObject = mediationNetworkEnumClass.GetStatic("NONE"); break; } return mediationNetworkObject; } /// /// Internal Helper Method. /// private static AndroidJavaObject convertDictionaryToJavaMap(Dictionary dictionary) { AndroidJavaObject map = new AndroidJavaObject("java.util.HashMap"); IntPtr putMethod = AndroidJNIHelper.GetMethodID(map.GetRawClass(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); jvalue[] val; if (dictionary != null) { foreach (var entry in dictionary) { val = AndroidJNIHelper.CreateJNIArgArray(new object[] { entry.Key, entry.Value }); AndroidJNI.CallObjectMethod(map.GetRawObject(), putMethod,val); AndroidJNI.DeleteLocalRef(val[0].l); AndroidJNI.DeleteLocalRef(val[1].l); } } return map; } } #endif } ================================================ FILE: Assets/AppsFlyer/AppsFlyerAndroid.cs.meta ================================================ fileFormatVersion: 2 guid: 172d18dd98e7e4ed3b30110568b0fae4 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerConsent.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { /// // Data class representing a user's consent for data processing in accordance with GDPR and DMA // (Digital Markets Act) compliance, specifically regarding advertising preferences. // This class should be used to notify and record the user's applicability // under GDPR, their general consent to data usage, and their consent to personalized // advertisements based on user data. /// ## Properties: /// - `isUserSubjectToGDPR` (optional) - Indicates whether GDPR regulations apply to the user. /// This may also serve as a general compliance flag for other regional regulations. /// - `hasConsentForDataUsage` (optional) - Indicates whether the user consents to the processing /// of their data for advertising purposes. /// - `hasConsentForAdsPersonalization` (optional) - Indicates whether the user consents to the /// use of their data for personalized advertising. /// - `hasConsentForAdStorage` (optional) - Indicates whether the user consents to ad-related storage access. /// /// **Usage Example:** /// ```csharp /// var consent = new AppsFlyerConsent( /// isUserSubjectToGDPR: true, /// hasConsentForDataUsage: true, /// hasConsentForAdsPersonalization: false, /// hasConsentForAdStorage: true /// ); /// **Deprecated APIs:** /// - `ForGDPRUser(...)` and `ForNonGDPRUser(...)` should no longer be used. /// - Use `new AppsFlyerConsent(...)` instead with relevant consent fields. /// /// public class AppsFlyerConsent { public bool? isUserSubjectToGDPR { get; private set; } public bool? hasConsentForDataUsage { get; private set; } public bool? hasConsentForAdsPersonalization { get; private set; } public bool? hasConsentForAdStorage { get; private set; } public AppsFlyerConsent( bool? isUserSubjectToGDPR = null, bool? hasConsentForDataUsage = null, bool? hasConsentForAdsPersonalization = null, bool? hasConsentForAdStorage = null) { this.isUserSubjectToGDPR = isUserSubjectToGDPR; this.hasConsentForDataUsage = hasConsentForDataUsage; this.hasConsentForAdsPersonalization = hasConsentForAdsPersonalization; this.hasConsentForAdStorage = hasConsentForAdStorage; } [Obsolete("Use the new constructor with optional booleans instead.")] private AppsFlyerConsent(bool isGDPR, bool hasForDataUsage, bool hasForAdsPersonalization) { isUserSubjectToGDPR = isGDPR; hasConsentForDataUsage = hasForDataUsage; hasConsentForAdsPersonalization = hasForAdsPersonalization; } [Obsolete("Use new AppsFlyerConsent(...) instead.")] public static AppsFlyerConsent ForGDPRUser(bool hasConsentForDataUsage, bool hasConsentForAdsPersonalization) { return new AppsFlyerConsent(true, hasConsentForDataUsage, hasConsentForAdsPersonalization); } [Obsolete("Use new AppsFlyerConsent(...) instead.")] public static AppsFlyerConsent ForNonGDPRUser() { return new AppsFlyerConsent(false); } } } ================================================ FILE: Assets/AppsFlyer/AppsFlyerConsent.cs.meta ================================================ fileFormatVersion: 2 guid: a97c986fe4ee0461badf7042e08db3f3 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerEventArgs.cs ================================================ using System; using System.Collections.Generic; namespace AppsFlyerSDK { /// /// Event args for AppsFlyer requests. /// Used for sessions and in-app events. /// Used to handle post request logic. /// /// Examples: /// statusCode / errorDescription /// /// 200 - null /// /// 10 - "Event timeout. Check 'minTimeBetweenSessions' param" /// 11 - "Skipping event because 'isStopTracking' enabled" /// 40 - Network error: Error description comes from Android /// 41 - "No dev key" /// 50 - "Status code failure" + actual response code from the server /// /// public class AppsFlyerRequestEventArgs : EventArgs { public AppsFlyerRequestEventArgs(int code, string description) { statusCode = code; errorDescription = description; } public int statusCode { get; } public string errorDescription { get; } } /// /// Event args for OnDeepLinkReceived. /// Used to handle deep linking results. /// public class DeepLinkEventsArgs : EventArgs { /// /// DeepLink dictionary to get additional parameters /// public Dictionary deepLink; /// /// DeepLink status: FOUND, NOT_FOUND, ERROR /// public DeepLinkStatus status { get; } /// /// DeepLink error: TIMEOUT, NETWORK, HTTP_STATUS_CODE, UNEXPECTED /// public DeepLinkError error { get; } public string getMatchType() { return getDeepLinkParameter("match_type"); } public string getDeepLinkValue() { return getDeepLinkParameter("deep_link_value"); } public string getClickHttpReferrer() { return getDeepLinkParameter("click_http_referrer"); } public string getMediaSource() { return getDeepLinkParameter("media_source"); } public string getCampaign() { return getDeepLinkParameter("campaign"); } public string getCampaignId() { return getDeepLinkParameter("campaign_id"); } public string getAfSub1() { return getDeepLinkParameter("af_sub1"); } public string getAfSub2() { return getDeepLinkParameter("af_sub2"); } public string getAfSub3() { return getDeepLinkParameter("af_sub3"); } public string getAfSub4() { return getDeepLinkParameter("af_sub4"); } public string getAfSub5() { return getDeepLinkParameter("af_sub5"); } public bool isDeferred() { if (deepLink != null && deepLink.ContainsKey("is_deferred")) { try { return (bool)deepLink["is_deferred"]; } catch (Exception e) { AppsFlyer.AFLog("DeepLinkEventsArgs.isDeferred", String.Format("{0} Exception caught.", e)); } } return false; } public Dictionary getDeepLinkDictionary() { return deepLink; } public DeepLinkEventsArgs(string str) { try { Dictionary dictionary = AppsFlyer.CallbackStringToDictionary(str); string status = ""; string error = ""; if (dictionary.ContainsKey("status") && dictionary["status"] != null) { status = dictionary["status"].ToString(); } if (dictionary.ContainsKey("error") && dictionary["error"] != null) { error = dictionary["error"].ToString(); } if (dictionary.ContainsKey("deepLink") && dictionary["deepLink"] != null) { this.deepLink = AppsFlyer.CallbackStringToDictionary(dictionary["deepLink"].ToString()); } if (dictionary.ContainsKey("is_deferred")) { this.deepLink["is_deferred"] = dictionary["is_deferred"]; } switch (status) { case "FOUND": this.status = DeepLinkStatus.FOUND; break; case "NOT_FOUND": this.status = DeepLinkStatus.NOT_FOUND; break; default: this.status = DeepLinkStatus.ERROR; break; } switch (error) { case "TIMEOUT": this.error = DeepLinkError.TIMEOUT; break; case "NETWORK": this.error = DeepLinkError.NETWORK; break; case "HTTP_STATUS_CODE": this.error = DeepLinkError.HTTP_STATUS_CODE; break; default: this.error = DeepLinkError.UNEXPECTED; break; } } catch (Exception e) { AppsFlyer.AFLog("DeepLinkEventsArgs.parseDeepLink", String.Format("{0} Exception caught.", e)); } } private string getDeepLinkParameter(string name) { if (deepLink != null && deepLink.ContainsKey(name) && deepLink[name] != null) { return deepLink[name].ToString(); } return null; } } public enum DeepLinkStatus { FOUND, NOT_FOUND, ERROR } public enum DeepLinkError { TIMEOUT, NETWORK, HTTP_STATUS_CODE, UNEXPECTED } } ================================================ FILE: Assets/AppsFlyer/AppsFlyerEventArgs.cs.meta ================================================ fileFormatVersion: 2 guid: a0fc241ad5a9b43a7b461a6147dbc74c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerObject.prefab ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1 &7315102894599890749 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 6207133488976360133} - component: {fileID: 4405976200006927252} m_Layer: 0 m_Name: AppsFlyerObject m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &6207133488976360133 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7315102894599890749} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &4405976200006927252 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7315102894599890749} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2a2ec6ba1ee8b48749524f015ed572a6, type: 3} m_Name: m_EditorClassIdentifier: devKey: appID: isDebug: 0 getConversionData: 0 ================================================ FILE: Assets/AppsFlyer/AppsFlyerObject.prefab.meta ================================================ fileFormatVersion: 2 guid: 0bfe3b149145747cc92dc53bb4df4e9b PrefabImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerObjectScript.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; using AppsFlyerSDK; // This class is intended to be used the the AppsFlyerObject.prefab public class AppsFlyerObjectScript : MonoBehaviour , IAppsFlyerConversionData { // These fields are set from the editor so do not modify! //******************************// public string devKey; public string appID; public string UWPAppID; public string macOSAppID; public bool isDebug; public bool getConversionData; //******************************// void Start() { // These fields are set from the editor so do not modify! //******************************// AppsFlyer.setIsDebug(isDebug); #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyer.initSDK(devKey, UWPAppID, getConversionData ? this : null); #elif UNITY_STANDALONE_OSX && !UNITY_EDITOR AppsFlyer.initSDK(devKey, macOSAppID, getConversionData ? this : null); #else AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null); #endif //******************************/ AppsFlyer.startSDK(); } void Update() { } // Mark AppsFlyer CallBacks public void onConversionDataSuccess(string conversionData) { AppsFlyer.AFLog("didReceiveConversionData", conversionData); Dictionary conversionDataDictionary = AppsFlyer.CallbackStringToDictionary(conversionData); // add deferred deeplink logic here } public void onConversionDataFail(string error) { AppsFlyer.AFLog("didReceiveConversionDataWithError", error); } public void onAppOpenAttribution(string attributionData) { AppsFlyer.AFLog("onAppOpenAttribution", attributionData); Dictionary attributionDataDictionary = AppsFlyer.CallbackStringToDictionary(attributionData); // add direct deeplink logic here } public void onAppOpenAttributionFailure(string error) { AppsFlyer.AFLog("onAppOpenAttributionFailure", error); } } ================================================ FILE: Assets/AppsFlyer/AppsFlyerObjectScript.cs.meta ================================================ fileFormatVersion: 2 guid: 2a2ec6ba1ee8b48749524f015ed572a6 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyerPurchaseConnector.cs ================================================ using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using System; namespace AppsFlyerSDK { public interface IAppsFlyerPurchaseRevenueDataSource { Dictionary PurchaseRevenueAdditionalParametersForProducts(HashSet products, HashSet transactions); } public interface IAppsFlyerPurchaseRevenueDataSourceStoreKit2 { Dictionary PurchaseRevenueAdditionalParametersStoreKit2ForProducts(HashSet products, HashSet transactions); } public class AppsFlyerPurchaseRevenueBridge : MonoBehaviour { #if UNITY_IOS && !UNITY_EDITOR [DllImport("__Internal")] private static extern void RegisterUnityPurchaseRevenueParamsCallback(Func callback); [DllImport("__Internal")] private static extern void RegisterUnityPurchaseRevenueParamsCallbackSK2(Func callback); #endif private static IAppsFlyerPurchaseRevenueDataSource _dataSource; private static IAppsFlyerPurchaseRevenueDataSourceStoreKit2 _dataSourceSK2; public static void RegisterDataSource(IAppsFlyerPurchaseRevenueDataSource dataSource) { _dataSource = dataSource; #if UNITY_IOS && !UNITY_EDITOR RegisterUnityPurchaseRevenueParamsCallback(GetAdditionalParameters); #elif UNITY_ANDROID && !UNITY_EDITOR using (AndroidJavaClass jc = new AndroidJavaClass("com.appsflyer.unity.PurchaseRevenueBridge")) { jc.CallStatic("setUnityBridge", new UnityPurchaseRevenueBridgeProxy()); } #endif } public static void RegisterDataSourceStoreKit2(IAppsFlyerPurchaseRevenueDataSourceStoreKit2 dataSource) { #if UNITY_IOS && !UNITY_EDITOR _dataSourceSK2 = dataSource; RegisterUnityPurchaseRevenueParamsCallbackSK2(GetAdditionalParametersSK2); #endif } public static Dictionary GetAdditionalParametersForAndroid(HashSet products, HashSet transactions) { return _dataSource?.PurchaseRevenueAdditionalParametersForProducts(products, transactions) ?? new Dictionary(); } #if UNITY_IOS && !UNITY_EDITOR [AOT.MonoPInvokeCallback(typeof(Func))] public static string GetAdditionalParameters(string productsJson, string transactionsJson) { try { HashSet products = new HashSet(); HashSet transactions = new HashSet(); if (!string.IsNullOrEmpty(productsJson)) { var dict = AFMiniJSON.Json.Deserialize(productsJson) as Dictionary; if (dict != null) { if (dict.TryGetValue("products", out var productsObj) && productsObj is List productList) products = new HashSet(productList); if (dict.TryGetValue("transactions", out var transactionsObj) && transactionsObj is List transactionList) transactions = new HashSet(transactionList); } } var parameters = _dataSource?.PurchaseRevenueAdditionalParametersForProducts(products, transactions) ?? new Dictionary(); return AFMiniJSON.Json.Serialize(parameters); } catch (Exception e) { Debug.LogError($"[AppsFlyer] Exception in GetAdditionalParameters: {e}"); return "{}"; } } #endif #if UNITY_IOS && !UNITY_EDITOR [AOT.MonoPInvokeCallback(typeof(Func))] public static string GetAdditionalParametersSK2(string productsJson, string transactionsJson) { try { HashSet products = new HashSet(); HashSet transactions = new HashSet(); if (!string.IsNullOrEmpty(productsJson)) { var dict = AFMiniJSON.Json.Deserialize(productsJson) as Dictionary; if (dict != null && dict.TryGetValue("products", out var productsObj) && productsObj is List productList) products = new HashSet(productList); } if (!string.IsNullOrEmpty(transactionsJson)) { var dict = AFMiniJSON.Json.Deserialize(transactionsJson) as Dictionary; if (dict != null && dict.TryGetValue("transactions", out var transactionsObj) && transactionsObj is List transactionList) transactions = new HashSet(transactionList); } var parameters = _dataSourceSK2?.PurchaseRevenueAdditionalParametersStoreKit2ForProducts(products, transactions) ?? new Dictionary(); return AFMiniJSON.Json.Serialize(parameters); } catch (Exception e) { Debug.LogError($"[AppsFlyer] Exception in GetAdditionalParametersSK2: {e}"); return "{}"; } } #endif } public class UnityPurchaseRevenueBridgeProxy : AndroidJavaProxy { public UnityPurchaseRevenueBridgeProxy() : base("com.appsflyer.unity.PurchaseRevenueBridge$UnityPurchaseRevenueBridge") { } public string getAdditionalParameters(string productsJson, string transactionsJson) { try { // Create empty sets if JSON is null or empty HashSet products = new HashSet(); HashSet transactions = new HashSet(); // Only try to parse if we have valid JSON if (!string.IsNullOrEmpty(productsJson)) { try { // First try to parse as a simple array var parsedProducts = AFMiniJSON.Json.Deserialize(productsJson); if (parsedProducts is List productList) { products = new HashSet(productList); } else if (parsedProducts is Dictionary dict) { if (dict.ContainsKey("events") && dict["events"] is List eventsList) { products = new HashSet(eventsList); } else { // If it's a dictionary but doesn't have events, add the whole dict products.Add(dict); } } } catch (Exception e) { Debug.LogError($"Error parsing products JSON: {e.Message}\nJSON: {productsJson}"); } } if (!string.IsNullOrEmpty(transactionsJson)) { try { // First try to parse as a simple array var parsedTransactions = AFMiniJSON.Json.Deserialize(transactionsJson); if (parsedTransactions is List transactionList) { transactions = new HashSet(transactionList); } else if (parsedTransactions is Dictionary dict) { if (dict.ContainsKey("events") && dict["events"] is List eventsList) { transactions = new HashSet(eventsList); } else { // If it's a dictionary but doesn't have events, add the whole dict transactions.Add(dict); } } } catch (Exception e) { Debug.LogError($"Error parsing transactions JSON: {e.Message}\nJSON: {transactionsJson}"); } } var parameters = AppsFlyerPurchaseRevenueBridge.GetAdditionalParametersForAndroid(products, transactions); return AFMiniJSON.Json.Serialize(parameters); } catch (Exception e) { Debug.LogError($"Error in getAdditionalParameters: {e.Message}\nProducts JSON: {productsJson}\nTransactions JSON: {transactionsJson}"); return "{}"; } } } public class AppsFlyerPurchaseConnector : MonoBehaviour { private static AppsFlyerPurchaseConnector instance; private Dictionary pendingParameters; private Action> pendingCallback; public static AppsFlyerPurchaseConnector Instance { get { if (instance == null) { GameObject go = new GameObject("AppsFlyerPurchaseConnector"); instance = go.AddComponent(); DontDestroyOnLoad(go); } return instance; } } private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } #if UNITY_ANDROID && !UNITY_EDITOR private static AndroidJavaClass appsFlyerAndroidConnector = new AndroidJavaClass("com.appsflyer.unity.AppsFlyerAndroidWrapper"); #endif public static void init(MonoBehaviour unityObject, Store s) { #if UNITY_IOS && !UNITY_EDITOR _initPurchaseConnector(unityObject.name); #elif UNITY_ANDROID && !UNITY_EDITOR int store = mapStoreToInt(s); appsFlyerAndroidConnector.CallStatic("initPurchaseConnector", unityObject ? unityObject.name : null, store); #endif } public static void build() { #if UNITY_IOS && !UNITY_EDITOR //not for iOS #elif UNITY_ANDROID && !UNITY_EDITOR appsFlyerAndroidConnector.CallStatic("build"); #else #endif } public static void startObservingTransactions() { #if UNITY_IOS && !UNITY_EDITOR _startObservingTransactions(); #elif UNITY_ANDROID && !UNITY_EDITOR appsFlyerAndroidConnector.CallStatic("startObservingTransactions"); #else #endif } public static void stopObservingTransactions() { #if UNITY_IOS && !UNITY_EDITOR _stopObservingTransactions(); #elif UNITY_ANDROID && !UNITY_EDITOR appsFlyerAndroidConnector.CallStatic("stopObservingTransactions"); #else #endif } public static void setIsSandbox(bool isSandbox) { #if UNITY_IOS && !UNITY_EDITOR _setIsSandbox(isSandbox); #elif UNITY_ANDROID && !UNITY_EDITOR appsFlyerAndroidConnector.CallStatic("setIsSandbox", isSandbox); #else #endif } public static void setPurchaseRevenueValidationListeners(bool enableCallbacks) { #if UNITY_IOS && !UNITY_EDITOR _setPurchaseRevenueDelegate(); #elif UNITY_ANDROID && !UNITY_EDITOR appsFlyerAndroidConnector.CallStatic("setPurchaseRevenueValidationListeners", enableCallbacks); #else #endif } public static void setAutoLogPurchaseRevenue(params AppsFlyerAutoLogPurchaseRevenueOptions[] autoLogPurchaseRevenueOptions) { #if UNITY_IOS && !UNITY_EDITOR int option = 0; foreach (AppsFlyerAutoLogPurchaseRevenueOptions op in autoLogPurchaseRevenueOptions) { option = option | (int)op; } _setAutoLogPurchaseRevenue(option); #elif UNITY_ANDROID && !UNITY_EDITOR if (autoLogPurchaseRevenueOptions.Length == 0) { return; } foreach (AppsFlyerAutoLogPurchaseRevenueOptions op in autoLogPurchaseRevenueOptions) { switch(op) { case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsDisabled: break; case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions: appsFlyerAndroidConnector.CallStatic("setAutoLogSubscriptions", true); break; case AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases: appsFlyerAndroidConnector.CallStatic("setAutoLogInApps", true); break; default: break; } } #else #endif } public static void setPurchaseRevenueDataSource(IAppsFlyerPurchaseRevenueDataSource dataSource) { #if UNITY_IOS && !UNITY_EDITOR if (dataSource != null) { _setPurchaseRevenueDataSource(dataSource.GetType().Name); AppsFlyerPurchaseRevenueBridge.RegisterDataSource(dataSource); } #elif UNITY_ANDROID && !UNITY_EDITOR if (dataSource != null) { AppsFlyerPurchaseRevenueBridge.RegisterDataSource(dataSource); } #endif } public static void setPurchaseRevenueDataSourceStoreKit2(IAppsFlyerPurchaseRevenueDataSourceStoreKit2 dataSourceSK2) { #if UNITY_IOS && !UNITY_EDITOR if (dataSourceSK2 != null) { AppsFlyerPurchaseRevenueBridge.RegisterDataSourceStoreKit2(dataSourceSK2); _setPurchaseRevenueDataSource("AppsFlyerObjectScript_StoreKit2"); } #endif } private static int mapStoreToInt(Store s) { switch(s) { case(Store.GOOGLE): return 0; default: return -1; } } public static void setStoreKitVersion(StoreKitVersion storeKitVersion) { #if UNITY_IOS && !UNITY_EDITOR _setStoreKitVersion((int)storeKitVersion); #elif UNITY_ANDROID && !UNITY_EDITOR // Android doesn't use StoreKit #else #endif } public static void logConsumableTransaction(string transactionJson) { #if UNITY_IOS && !UNITY_EDITOR _logConsumableTransaction(transactionJson); #elif UNITY_ANDROID && !UNITY_EDITOR // Android doesn't use StoreKit #else #endif } #if UNITY_IOS && !UNITY_EDITOR [DllImport("__Internal")] private static extern void _startObservingTransactions(); [DllImport("__Internal")] private static extern void _stopObservingTransactions(); [DllImport("__Internal")] private static extern void _setIsSandbox(bool isSandbox); [DllImport("__Internal")] private static extern void _setPurchaseRevenueDelegate(); [DllImport("__Internal")] private static extern void _setPurchaseRevenueDataSource(string dataSourceName); [DllImport("__Internal")] private static extern void _setAutoLogPurchaseRevenue(int option); [DllImport("__Internal")] private static extern void _initPurchaseConnector(string objectName); [DllImport("__Internal")] private static extern void _setStoreKitVersion(int storeKitVersion); [DllImport("__Internal")] private static extern void _logConsumableTransaction(string transactionJson); #endif } public enum Store { GOOGLE = 0 } public enum AppsFlyerAutoLogPurchaseRevenueOptions { AppsFlyerAutoLogPurchaseRevenueOptionsDisabled = 0, AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions = 1 << 0, AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases = 1 << 1 } public enum StoreKitVersion { SK1 = 0, SK2 = 1 } } ================================================ FILE: Assets/AppsFlyer/AppsFlyerPurchaseConnector.cs.meta ================================================ fileFormatVersion: 2 guid: 0636ea07d370d437183f3762280c08ce MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/AppsFlyeriOS.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; using System.Reflection; namespace AppsFlyerSDK { #if UNITY_IOS || UNITY_STANDALONE_OSX public class AppsFlyeriOS: IAppsFlyerIOSBridge { public bool isInit { get; set; } public AppsFlyeriOS() { } public AppsFlyeriOS(string devKey, string appID, MonoBehaviour gameObject) { setAppsFlyerDevKey(devKey); setAppleAppID(appID); if (gameObject != null) { #if UNITY_IOS getConversionData(gameObject.name); #elif UNITY_STANDALONE_OSX getConversionData(gameObject.GetType().ToString()); #endif } } /// /// Start Session. /// This will record a session and then record all background forground sessions during the lifecycle of the app. /// public void startSDK(bool shouldCallback, string CallBackObjectName) { #if UNITY_STANDALONE_OSX && !UNITY_EDITOR _startSDK(shouldCallback, CallBackObjectName, getCallback); #elif UNITY_IOS && !UNITY_EDITOR _startSDK(shouldCallback, CallBackObjectName); #endif } /// /// Send an In-App Event. /// In-App Events provide insight on what is happening in your app. /// /// Name of event. /// Contains dictionary of values for handling by backend. public void sendEvent(string eventName, Dictionary eventValues) { sendEvent(eventName, eventValues, false, AppsFlyer.CallBackObjectName); } public void sendEvent(string eventName, Dictionary eventValues, bool shouldCallback, string callBackObjectName) { #if !UNITY_EDITOR _afSendEvent(eventName, AFMiniJSON.Json.Serialize(eventValues), shouldCallback, callBackObjectName); #endif } /// /// Get the conversion data. /// Allows the developer to access the user attribution data in real-time for every new install, directly from the SDK level. /// By doing this you can serve users with personalized content or send them to specific activities within the app, /// which can greatly enhance their engagement with your app. /// public void getConversionData(string objectName) { #if !UNITY_EDITOR _getConversionData(objectName); #endif } /// /// In case you use your own user ID in your app, you can set this property to that ID. /// Enables you to cross-reference your own unique ID with AppsFlyer’s unique ID and the other devices’ IDs. /// /// Customer ID for client. public void setCustomerUserId(string customerUserID) { #if !UNITY_EDITOR _setCustomerUserID(customerUserID); #endif } /// /// In case you use custom data and you want to receive it in the raw reports. /// see [Setting additional custom data] (https://support.appsflyer.com/hc/en-us/articles/207032066-AppsFlyer-SDK-Integration-iOS#setting-additional-custom-data) for more information. /// /// additional data Dictionary. public void setAdditionalData(Dictionary customData) { #if !UNITY_EDITOR _setAdditionalData(AFMiniJSON.Json.Serialize(customData)); #endif } /// /// Use this method to set your AppsFlyer's dev key. /// /// AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. public void setAppsFlyerDevKey(string appsFlyerDevKey) { #if !UNITY_EDITOR _setAppsFlyerDevKey(appsFlyerDevKey); #endif } /// /// Use this method to set your app's Apple ID(taken from the app's page on iTunes Connect). /// /// your app's Apple ID. public void setAppleAppID(string appleAppID) { #if !UNITY_EDITOR _setAppleAppID(appleAppID); #endif } /// /// Setting user local currency code for in-app purchases. /// The currency code should be a 3 character ISO 4217 code. (default is USD). /// You can set the currency code for all events by calling the following method. /// /// 3 character ISO 4217 code. public void setCurrencyCode(string currencyCode) { #if !UNITY_EDITOR _setCurrencyCode(currencyCode); #endif } /// /// AppsFlyer SDK collect Apple's `advertisingIdentifier` if the `AdSupport.framework` included in the SDK. /// You can disable this behavior by setting the following property to true. /// /// boolean to disableCollectAppleAdSupport public void setDisableCollectAppleAdSupport(bool disableCollectAppleAdSupport) { #if !UNITY_EDITOR _setDisableCollectAppleAdSupport(disableCollectAppleAdSupport); #endif } /// /// Enables Debug logs for the AppsFlyer SDK. /// Should only be set to true in development / debug. /// The default value is false. /// /// shouldEnable boolean.. public void setIsDebug(bool isDebug) { #if !UNITY_EDITOR _setIsDebug(isDebug); #endif } /// /// Set this flag to true, to collect the current device name(e.g. "My iPhone"). Default value is false. /// /// boolean shouldCollectDeviceName. [System.Obsolete("This is deprecated")] public void setShouldCollectDeviceName(bool shouldCollectDeviceName) { #if !UNITY_EDITOR _setShouldCollectDeviceName(shouldCollectDeviceName); #endif } /// /// Set the OneLink ID that should be used for User-Invites. /// The link that is generated for the user invite will use this OneLink as the base link. /// /// OneLink ID obtained from the AppsFlyer Dashboard. public void setAppInviteOneLinkID(string appInviteOneLinkID) { #if !UNITY_EDITOR _setAppInviteOneLinkID(appInviteOneLinkID); #endif } /// /// Set the deepLink timeout value that should be used for DDL. /// /// deepLink timeout in milliseconds. public void setDeepLinkTimeout(long deepLinkTimeout) { #if !UNITY_EDITOR _setDeepLinkTimeout(deepLinkTimeout); #endif } /// /// Calling enableTCFDataCollection(true) will enable collecting and sending any TCF related data. /// Calling enableTCFDataCollection(false) will disable the collection of TCF related data and from sending it. /// /// should start TCF Data collection boolean. public void enableTCFDataCollection(bool shouldCollectTcfData) { #if !UNITY_EDITOR _enableTCFDataCollection(shouldCollectTcfData); #endif } /// /// Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage purposes within the application. /// /// instance of AppsFlyerConsent. public void setConsentData(AppsFlyerConsent appsFlyerConsent) { #if !UNITY_EDITOR string isUserSubjectToGDPR = appsFlyerConsent.isUserSubjectToGDPR?.ToString().ToLower() ?? "null"; string hasConsentForDataUsage = appsFlyerConsent.hasConsentForDataUsage?.ToString().ToLower() ?? "null"; string hasConsentForAdsPersonalization = appsFlyerConsent.hasConsentForAdsPersonalization?.ToString().ToLower() ?? "null"; string hasConsentForAdStorage = appsFlyerConsent.hasConsentForAdStorage?.ToString().ToLower() ?? "null"; _setConsentData(isUserSubjectToGDPR, hasConsentForDataUsage, hasConsentForAdsPersonalization, hasConsentForAdStorage); #endif } /// /// Logs ad revenue data along with additional parameters if provided. /// /// instance of AFAdRevenueData containing ad revenue information. /// An optional map of additional parameters to be logged with ad revenue data. This can be null if there are no additional parameters. public void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary additionalParameters) { #if !UNITY_EDITOR _logAdRevenue(adRevenueData.monetizationNetwork, adRevenueData.mediationNetwork, adRevenueData.currencyIso4217Code, adRevenueData.eventRevenue, AFMiniJSON.Json.Serialize(additionalParameters)); #endif } /// /// Anonymize user Data. /// Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions. /// Default is false /// /// boolean shouldAnonymizeUser. public void anonymizeUser(bool shouldAnonymizeUser) { #if !UNITY_EDITOR _anonymizeUser(shouldAnonymizeUser); #endif } /// /// Opt-out for Apple Search Ads attributions. /// /// boolean disableCollectIAd. public void setDisableCollectIAd(bool disableCollectIAd) { #if !UNITY_EDITOR _setDisableCollectIAd(disableCollectIAd); #endif } /// /// In app purchase receipt validation Apple environment(production or sandbox). The default value is false. /// /// boolean useReceiptValidationSandbox. public void setUseReceiptValidationSandbox(bool useReceiptValidationSandbox) { #if !UNITY_EDITOR _setUseReceiptValidationSandbox(useReceiptValidationSandbox); #endif } /// /// Set this flag to test uninstall on Apple environment(production or sandbox). The default value is false. /// /// boolean useUninstallSandbox. public void setUseUninstallSandbox(bool useUninstallSandbox) { #if !UNITY_EDITOR _setUseUninstallSandbox(useUninstallSandbox); #endif } /// /// For advertisers who wrap OneLink within another Universal Link. /// An advertiser will be able to deeplink from a OneLink wrapped within another Universal Link and also record this retargeting conversion. /// /// Array of urls. public void setResolveDeepLinkURLs(params string[] resolveDeepLinkURLs) { #if !UNITY_EDITOR _setResolveDeepLinkURLs(resolveDeepLinkURLs.Length,resolveDeepLinkURLs); #endif } /// /// For advertisers who use vanity OneLinks. /// /// Array of domains. public void setOneLinkCustomDomain(params string[] oneLinkCustomDomains) { #if !UNITY_EDITOR _setOneLinkCustomDomains(oneLinkCustomDomains.Length, oneLinkCustomDomains); #endif } /// /// Set the user emails and encrypt them. /// cryptMethod Encryption method: /// EmailCryptType.EmailCryptTypeMD5 /// EmailCryptType.EmailCryptTypeSHA1 /// EmailCryptType.EmailCryptTypeSHA256 /// EmailCryptType.EmailCryptTypeNone /// /// type Hash algoritm. /// length of userEmails array. /// userEmails The list of strings that hold mails.} public void setUserEmails(EmailCryptType cryptType, params string[] userEmails) { #if !UNITY_EDITOR _setUserEmails(cryptType, userEmails.Length, userEmails); #endif } /// /// Set the user phone number. /// /// User phoneNumber. public void setPhoneNumber(string phoneNumber){ #if !UNITY_EDITOR _setPhoneNumber(phoneNumber); #endif } /// /// [Deprecated] To send and validate in app purchases - please use V2 with AFSDKPurchaseDetailsIOS instead. /// /// The product identifier. /// The product price. /// The product currency. /// The purchase transaction Id. /// The additional param, which you want to receive it in the raw reports. [System.Obsolete("This method is deprecated. Use validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) instead.")] public void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, Dictionary additionalParameters, MonoBehaviour gameObject) { #if !UNITY_EDITOR _validateAndSendInAppPurchase(productIdentifier, price, currency, transactionId, AFMiniJSON.Json.Serialize(additionalParameters), gameObject ? gameObject.name : null); #endif } /// /// V2 - To send and validate in app purchases you can call this method from the processPurchase method. /// /// The AFSDKPurchaseDetailsIOS instance. /// The additional params, which you want to receive it in the raw reports. public void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject) { #if !UNITY_EDITOR _validateAndSendInAppPurchaseV2(details.productId, details.transactionId, (int)details.purchaseType, AFMiniJSON.Json.Serialize(purchaseAdditionalDetails), gameObject ? gameObject.name : null); #endif } /// /// To record location for geo-fencing. Does the same as code below. /// /// The location longitude. /// The location latitude. public void recordLocation(double longitude, double latitude) { #if !UNITY_EDITOR _recordLocation(longitude, latitude); #endif } /// /// Get AppsFlyer's unique device ID, which is created for every new install of an app. /// public string getAppsFlyerId() { #if !UNITY_EDITOR return _getAppsFlyerId(); #else return ""; #endif } /// /// Register uninstall - you should register for remote notification and provide AppsFlyer the push device token. /// /// deviceToken The `deviceToken` from `-application:didRegisterForRemoteNotificationsWithDeviceToken:`. public void registerUninstall(byte[] deviceToken) { #if !UNITY_EDITOR _registerUninstall(deviceToken); #endif } /// /// Enable AppsFlyer to handle a push notification. /// /// pushPayload The `userInfo` from received remote notification. One of root keys should be @"af".. public void handlePushNotification(Dictionary pushPayload) { #if !UNITY_EDITOR _handlePushNotification(AFMiniJSON.Json.Serialize(pushPayload)); #endif } /// /// Get SDK version. /// public string getSdkVersion() { #if !UNITY_EDITOR return _getSDKVersion(); #else return ""; #endif } /// /// This property accepts a string value representing the host name for all endpoints. /// Can be used to Zero rate your application’s data usage.Contact your CSM for more information. /// /// Host Name. /// Host prefix. public void setHost(string hostPrefix, string host) { #if !UNITY_EDITOR _setHost(host, hostPrefix); #endif } /// /// This property is responsible for timeout between sessions in seconds. /// Default value is 5 seconds. /// /// minimum time between 2 separate sessions in seconds. public void setMinTimeBetweenSessions(int minTimeBetweenSessions) { #if !UNITY_EDITOR _setMinTimeBetweenSessions(minTimeBetweenSessions); #endif } /// /// Once this API is invoked, our SDK no longer communicates with our servers and stops functioning. /// In some extreme cases you might want to shut down all SDK activity due to legal and privacy compliance. /// This can be achieved with the stopSDK API. /// /// boolean isSDKStopped. public void stopSDK(bool isSDKStopped) { #if !UNITY_EDITOR _stopSDK(isSDKStopped); #endif } // /// Was the stopSDK(boolean) API set to true. /// /// boolean isSDKStopped. public bool isSDKStopped() { #if !UNITY_EDITOR return _isSDKStopped(); #else return false; #endif } /// /// In case you want to track deep linking manually call handleOpenUrl. /// The continueUserActivity and onOpenURL are implemented in the AppsFlyerAppController.mm class, so /// only use this method if the other methods do not cover your apps deeplinking needs. /// /// The URL to be passed to your AppDelegate. /// The sourceApplication to be passed to your AppDelegate. /// The annotation to be passed to your app delegate. public void handleOpenUrl(string url, string sourceApplication, string annotation) { #if !UNITY_EDITOR _handleOpenUrl(url, sourceApplication, annotation); #endif } /// /// Used by advertisers to exclude all networks/integrated partners from getting data. /// public void setSharingFilterForAllPartners() { #if !UNITY_EDITOR _setSharingFilterForAllPartners(); #endif } /// /// Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data. /// /// partners to exclude from getting data public void setSharingFilter(params string[] partners) { #if !UNITY_EDITOR _setSharingFilter(partners.Length, partners); #endif } /// /// Lets you configure how which partners should the SDK exclude from data-sharing. /// partners to exclude from getting data public static void setSharingFilterForPartners(params string[] partners) { #if !UNITY_EDITOR _setSharingFilterForPartners(partners.Length, partners); #endif } /// /// To record an impression use the following API call. /// Make sure to use the promoted App ID as it appears within the AppsFlyer dashboard. /// /// promoted App ID. /// cross promotion campaign. /// parameters Dictionary. public void recordCrossPromoteImpression(string appID, string campaign, Dictionary parameters) { #if !UNITY_EDITOR _recordCrossPromoteImpression(appID, campaign, AFMiniJSON.Json.Serialize(parameters)); #endif } /// /// Use the following API to attribute the click and launch the app store's app page. /// /// promoted App ID /// cross promotion campaign /// additional user params public void attributeAndOpenStore(string appID, string campaign, Dictionary parameters, MonoBehaviour gameObject) { #if !UNITY_EDITOR _attributeAndOpenStore(appID, campaign, AFMiniJSON.Json.Serialize(parameters), gameObject ? gameObject.name : null); #endif } /// /// The LinkGenerator class builds the invite URL according to various setter methods which allow passing on additional information on the click. /// See - https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution- /// /// parameters Dictionary. public void generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject) { #if !UNITY_EDITOR _generateUserInviteLink(AFMiniJSON.Json.Serialize(parameters), gameObject ? gameObject.name : null); #endif } /// /// It is recommended to generate an in-app event after the invite is sent to record the invites from the senders' perspective. /// This enables you to find the users that tend most to invite friends, and the media sources that get you these users. /// /// channel string. /// parameters Dictionary.. public void recordInvite(string channel, Dictionary parameters) { #if !UNITY_EDITOR _recordInvite(channel, AFMiniJSON.Json.Serialize(parameters)); #endif } /// /// Waits for request user authorization to access app-related data /// /// time to wait until session starts public void waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval) { #if !UNITY_EDITOR _waitForATTUserAuthorizationWithTimeoutInterval(timeoutInterval); #endif } /// /// /// bool should diable public void disableSKAdNetwork(bool isDisabled) { #if !UNITY_EDITOR _disableSKAdNetwork(isDisabled); #endif } /// /// Use this method if you’re integrating your app with push providers /// that don’t use the default push notification JSON schema the SDK expects. /// See docs for more info. /// /// array of nested json path public void addPushNotificationDeepLinkPath(params string[] paths) { #if !UNITY_EDITOR _addPushNotificationDeepLinkPath(paths.Length, paths); #endif } /// /// subscribe to unified deep link callbacks /// public void subscribeForDeepLink(string objectName){ #if !UNITY_EDITOR _subscribeForDeepLink(objectName); #endif } /// /// Set the language of the device. /// public void setCurrentDeviceLanguage(string language){ #if !UNITY_EDITOR _setCurrentDeviceLanguage(language); #endif } /// /// Allows sending custom data for partner integration purposes. /// public void setPartnerData(string partnerId, Dictionary partnerInfo){ #if !UNITY_EDITOR _setPartnerData(partnerId, AFMiniJSON.Json.Serialize(partnerInfo)); #endif } /// /// Use to disable app vendor identifier (IDFV) collection, 'true' to disable. /// public void disableIDFVCollection(bool isDisabled){ #if !UNITY_EDITOR _disableIDFVCollection(isDisabled); #endif } delegate void unityCallBack(string message); [AOT.MonoPInvokeCallback(typeof(unityCallBack))] public static void getCallback(string gameObjectName, string callbackName, string message) { GameObject go = GameObject.Find("AppsFlyerObject"); var afscript = go.GetComponent("AppsFlyerObjectScript") as AppsFlyerObjectScript; Type type = typeof(AppsFlyerObjectScript); MethodInfo info = type.GetMethod(callbackName); info.Invoke(afscript, new[] { message }); } /* * AppsFlyer ios method mapping */ #if UNITY_IOS [DllImport("__Internal")] private static extern void _startSDK(bool shouldCallback, string objectName); #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] private static extern void _startSDK(bool shouldCallback, string objectName, Action getCallback); #endif #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _getConversionData(string objectName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setCustomerUserID(string customerUserID); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setAdditionalData(string customData); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setAppsFlyerDevKey(string appsFlyerDevKey); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setAppleAppID(string appleAppID); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setCurrencyCode(string currencyCode); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setDisableCollectAppleAdSupport(bool disableCollectAppleAdSupport); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setIsDebug(bool isDebug); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setShouldCollectDeviceName(bool shouldCollectDeviceName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setAppInviteOneLinkID(string appInviteOneLinkID); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setDeepLinkTimeout(long deepLinkTimeout); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _anonymizeUser(bool shouldAnonymizeUser); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _enableTCFDataCollection(bool shouldCollectTcfData); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setConsentData(string isUserSubjectToGDPR, string hasConsentForDataUsage, string hasConsentForAdsPersonalization, string hasConsentForAdStorage); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _logAdRevenue(string monetizationNetwork, MediationNetwork mediationNetwork, string currencyIso4217Code, double eventRevenue, string additionalParameters); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setDisableCollectIAd(bool disableCollectIAd); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setUseReceiptValidationSandbox(bool useReceiptValidationSandbox); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setUseUninstallSandbox(bool useUninstallSandbox); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setResolveDeepLinkURLs(int length, params string[] resolveDeepLinkURLs); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setOneLinkCustomDomains(int length, params string[] oneLinkCustomDomains); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setUserEmails(EmailCryptType cryptType, int length, params string[] userEmails); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setPhoneNumber(string phoneNumber); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _afSendEvent(string eventName, string eventValues, bool shouldCallback, string objectName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, string additionalParameters, string objectName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _validateAndSendInAppPurchaseV2(string product, string transactionId, int purchaseType, string purchaseAdditionalDetails, string objectName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _recordLocation(double longitude, double latitude); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern string _getAppsFlyerId(); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _registerUninstall(byte[] deviceToken); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _handlePushNotification(string pushPayload); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern string _getSDKVersion(); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setHost(string host, string hostPrefix); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setMinTimeBetweenSessions(int minTimeBetweenSessions); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _stopSDK(bool isStopSDK); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern bool _isSDKStopped(); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _handleOpenUrl(string url, string sourceApplication, string annotation); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setSharingFilterForAllPartners(); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setSharingFilter(int length, params string[] partners); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setSharingFilterForPartners(int length, params string[] partners); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _recordCrossPromoteImpression(string appID, string campaign, string parameters); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _attributeAndOpenStore(string appID, string campaign, string parameters, string gameObject); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _generateUserInviteLink(string parameters, string gameObject); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _recordInvite(string channel, string parameters); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _disableSKAdNetwork(bool isDisabled); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _addPushNotificationDeepLinkPath(int length, params string[] paths); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _subscribeForDeepLink(string objectName); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setCurrentDeviceLanguage(string language); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _setPartnerData(string partnerId, string partnerInfo); #if UNITY_IOS [DllImport("__Internal")] #elif UNITY_STANDALONE_OSX [DllImport("AppsFlyerBundle")] #endif private static extern void _disableIDFVCollection(bool isDisabled); } #endif } ================================================ FILE: Assets/AppsFlyer/AppsFlyeriOS.cs.meta ================================================ fileFormatVersion: 2 guid: b34371b3cc09641ebb007ffc4e9500f0 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyer.Editor.asmdef ================================================ { "name": "AppsFlyer.Editor", "references": [ "AppsFlyer" ], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyer.Editor.asmdef.meta ================================================ fileFormatVersion: 2 guid: d008146f00dea44d38752b4289e5f65b AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml ================================================ ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml.meta ================================================ fileFormatVersion: 2 guid: a03558dbbfeac45db9afe9e9c2df5a85 TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyerObjectEditor.cs ================================================ using UnityEditor; using UnityEngine; [CustomEditor(typeof(AppsFlyerObjectScript))] [CanEditMultipleObjects] public class AppsFlyerObjectEditor : Editor { SerializedProperty devKey; SerializedProperty appID; SerializedProperty UWPAppID; SerializedProperty macOSAppID; SerializedProperty isDebug; SerializedProperty getConversionData; void OnEnable() { devKey = serializedObject.FindProperty("devKey"); appID = serializedObject.FindProperty("appID"); UWPAppID = serializedObject.FindProperty("UWPAppID"); macOSAppID = serializedObject.FindProperty("macOSAppID"); isDebug = serializedObject.FindProperty("isDebug"); getConversionData = serializedObject.FindProperty("getConversionData"); } public override void OnInspectorGUI() { serializedObject.Update(); DrawLogo(); EditorGUILayout.Separator(); EditorGUILayout.HelpBox("Set your devKey and appID to init the AppsFlyer SDK and start tracking. You must modify these fields and provide:\ndevKey - Your application devKey provided by AppsFlyer.\nappId - For iOS only. Your iTunes Application ID.\nUWP app id - For UWP only. Your application app id \nMac OS app id - For MacOS app only.", MessageType.Info); EditorGUILayout.PropertyField(devKey); EditorGUILayout.PropertyField(appID); EditorGUILayout.PropertyField(UWPAppID); EditorGUILayout.PropertyField(macOSAppID); EditorGUILayout.Separator(); EditorGUILayout.HelpBox("Enable get conversion data to allow your app to recive deeplinking callbacks", MessageType.None); EditorGUILayout.PropertyField(getConversionData); EditorGUILayout.Separator(); EditorGUILayout.HelpBox("Debugging should be restricted to development phase only.\n Do not distribute the app to app stores with debugging enabled", MessageType.Warning); EditorGUILayout.PropertyField(isDebug); EditorGUILayout.Separator(); EditorGUILayout.HelpBox("For more information on setting up AppsFlyer check out our relevant docs.", MessageType.None); if (GUILayout.Button("AppsFlyer Unity Docs", new GUILayoutOption[] { GUILayout.Width(200) })) { Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/213766183-Unity-SDK-integration-for-developers"); } if (GUILayout.Button("AppsFlyer Android Docs", new GUILayoutOption[] { GUILayout.Width(200) })) { Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032126-Android-SDK-integration-for-developers"); } if (GUILayout.Button("AppsFlyer iOS Docs", new GUILayoutOption[] { GUILayout.Width(200) })) { Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032066-AppsFlyer-SDK-Integration-iOS"); } if (GUILayout.Button("AppsFlyer Deeplinking Docs", new GUILayoutOption[] { GUILayout.Width(200) })) { Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-deep-linking-guide#Setups"); } if (GUILayout.Button("AppsFlyer Windows Docs", new GUILayoutOption[] { GUILayout.Width(200) })) { Application.OpenURL("https://support.appsflyer.com/hc/en-us/articles/207032026-Windows-and-Xbox-SDK-integration-for-developers"); } serializedObject.ApplyModifiedProperties(); } private void DrawLogo() { var guids = AssetDatabase.FindAssets("appsflyer_logo"); if (guids.Length == 0) return; Texture logo = (Texture)AssetDatabase.LoadAssetAtPath( AssetDatabase.GUIDToAssetPath(guids[0]), typeof(Texture)); if (logo == null) return; float maxWidth = Mathf.Min(200, EditorGUIUtility.currentViewWidth - 40); float aspect = (float)logo.height / logo.width; float height = maxWidth * aspect; Rect rect = GUILayoutUtility.GetRect(maxWidth, height, GUILayout.ExpandWidth(false)); rect.x = (EditorGUIUtility.currentViewWidth - maxWidth) * 0.5f; rect.width = maxWidth; GUI.DrawTexture(rect, logo, ScaleMode.ScaleToFit); } } ================================================ FILE: Assets/AppsFlyer/Editor/AppsFlyerObjectEditor.cs.meta ================================================ fileFormatVersion: 2 guid: d248a134cf494486fb1d6a2e95a05d87 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Editor/appsflyer_logo.png.meta ================================================ fileFormatVersion: 2 guid: bc7fa5a6b64b944a4b2900fd877acb8b TextureImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 11 mipmaps: mipMapMode: 0 enableMipMap: 1 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 borderMipMap: 0 mipMapsPreserveCoverage: 0 alphaTestReferenceValue: 0.5 mipMapFadeDistanceStart: 1 mipMapFadeDistanceEnd: 3 bumpmap: convertToNormalMap: 0 externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 seamlessCubemap: 0 textureFormat: 1 maxTextureSize: 2048 textureSettings: serializedVersion: 2 filterMode: -1 aniso: -1 mipBias: -100 wrapU: -1 wrapV: -1 wrapW: -1 nPOTScale: 1 lightmap: 0 compressionQuality: 50 spriteMode: 0 spriteExtrude: 1 spriteMeshType: 1 alignment: 0 spritePivot: {x: 0.5, y: 0.5} spritePixelsToUnits: 100 spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 alphaIsTransparency: 0 spriteTessellationDetail: -1 textureType: 0 textureShape: 1 singleChannelComponent: 0 maxTextureSizeSet: 0 compressionQualitySet: 0 textureFormatSet: 0 applyGammaDecoding: 0 platformSettings: - serializedVersion: 3 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 textureFormat: -1 textureCompression: 1 compressionQuality: 50 crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] physicsShape: [] bones: [] spriteID: internalID: 0 vertices: [] indices: edges: [] weights: [] secondaryTextures: [] spritePackingTag: pSDRemoveMatte: 0 pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Editor.meta ================================================ fileFormatVersion: 2 guid: 9f84c02aa78da4ac9b444d98d97f7cc4 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerAndroidBridge.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace AppsFlyerSDK { public interface IAppsFlyerAndroidBridge : IAppsFlyerNativeBridge { void updateServerUninstallToken(string token); void setImeiData(string imei); void setAndroidIdData(string androidId); void waitForCustomerUserId(bool wait); void setCustomerIdAndStartSDK(string id); string getOutOfStore(); void setOutOfStore(string sourceName); void setCollectAndroidID(bool isCollect); void setCollectIMEI(bool isCollect); void setIsUpdate(bool isUpdate); void setPreinstallAttribution(string mediaSource, string campaign, string siteId); bool isPreInstalledApp(); string getAttributionId(); void handlePushNotifications(); void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary additionalParameters, MonoBehaviour gameObject); void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject); void setCollectOaid(bool isCollect); void setDisableAdvertisingIdentifiers(bool disable); void setDisableNetworkData(bool disable); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerAndroidBridge.cs.meta ================================================ fileFormatVersion: 2 guid: cdf9d1bc41a8244b3bc2d249fb6cd7aa MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerConversionData.cs ================================================ namespace AppsFlyerSDK { public interface IAppsFlyerConversionData { /// /// `conversionData` contains information about install. Organic/non-organic, etc. /// https://support.appsflyer.com/hc/en-us/articles/360000726098-Conversion-Data-Scenarios#Introduction /// /// JSON string of the returned conversion data. void onConversionDataSuccess(string conversionData); /// /// Any errors that occurred during the conversion request. /// /// A string describing the error. void onConversionDataFail(string error); /// /// `attributionData` contains information about OneLink, deeplink. /// https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-Deep-Linking-Guide#Intro /// /// JSON string of the returned deeplink data. void onAppOpenAttribution(string attributionData); /// /// Any errors that occurred during the attribution request. /// /// A string describing the error. void onAppOpenAttributionFailure(string error); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerConversionData.cs.meta ================================================ fileFormatVersion: 2 guid: d2f1d4dadb7cb44628f25f1ffd8fc104 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerIOSBridge.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace AppsFlyerSDK { public interface IAppsFlyerIOSBridge : IAppsFlyerNativeBridge { void setDisableCollectAppleAdSupport(bool disable); void setShouldCollectDeviceName(bool shouldCollectDeviceName); void setDisableCollectIAd(bool disableCollectIAd); void setUseReceiptValidationSandbox(bool useReceiptValidationSandbox); void setUseUninstallSandbox(bool useUninstallSandbox); void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, Dictionary additionalParameters, MonoBehaviour gameObject); void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject); void registerUninstall(byte[] deviceToken); void handleOpenUrl(string url, string sourceApplication, string annotation); void waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval); void setCurrentDeviceLanguage(string language); void disableSKAdNetwork(bool isDisabled); void disableIDFVCollection(bool isDisabled); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerIOSBridge.cs.meta ================================================ fileFormatVersion: 2 guid: 6847fb337898040288c165e3667101a3 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerNativeBridge.cs ================================================ using System.Collections; using System.Collections.Generic; using UnityEngine; namespace AppsFlyerSDK { public interface IAppsFlyerNativeBridge { bool isInit { get; set; } void startSDK(bool onRequestResponse, string CallBackObjectName); void sendEvent(string eventName, Dictionary eventValues, bool onInAppResponse, string CallBackObjectName); void stopSDK(bool isSDKStopped); bool isSDKStopped(); string getSdkVersion(); void setCustomerUserId(string id); void setAppInviteOneLinkID(string oneLinkId); void setAdditionalData(Dictionary customData); void setDeepLinkTimeout(long deepLinkTimeout); void setResolveDeepLinkURLs(params string[] urls); void setOneLinkCustomDomain(params string[] domains); void setCurrencyCode(string currencyCode); void recordLocation(double latitude, double longitude); void anonymizeUser(bool shouldAnonymizeUser); string getAppsFlyerId(); void enableTCFDataCollection(bool shouldCollectTcfData); void setConsentData(AppsFlyerConsent appsFlyerConsent); void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary additionalParameters); void setMinTimeBetweenSessions(int seconds); void setHost(string hostPrefixName, string hostName); void setPhoneNumber(string phoneNumber); void setSharingFilterForAllPartners(); void setSharingFilter(params string[] partners); void getConversionData(string objectName); void attributeAndOpenStore(string appID, string campaign, Dictionary userParams, MonoBehaviour gameObject); void recordCrossPromoteImpression(string appID, string campaign, Dictionary parameters); void generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject); void addPushNotificationDeepLinkPath(params string[] paths); void setUserEmails(EmailCryptType cryptType, params string[] userEmails); void subscribeForDeepLink(string objectName); void setIsDebug(bool shouldEnable); void setPartnerData(string partnerId, Dictionary partnerInfo); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerNativeBridge.cs.meta ================================================ fileFormatVersion: 2 guid: 409b8302434664a3785ce55d075e7f58 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerPurchaseValidation.cs ================================================ namespace AppsFlyerSDK { public interface IAppsFlyerPurchaseValidation { void didReceivePurchaseRevenueValidationInfo(string validationInfo); void didReceivePurchaseRevenueError(string error); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerPurchaseValidation.cs.meta ================================================ fileFormatVersion: 2 guid: 7c60f499ae0d048b1be8ffd6878a184c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerUserInvite.cs ================================================ namespace AppsFlyerSDK { public interface IAppsFlyerUserInvite { /// /// The success callback for generating OneLink URLs. /// /// A string of the newly created url. void onInviteLinkGenerated(string link); /// /// The error callback for generating OneLink URLs /// /// A string describing the error. void onInviteLinkGeneratedFailure(string error); /// /// (ios only) iOS allows you to utilize the StoreKit component to open /// the App Store while remaining in the context of your app. /// More details at https://support.appsflyer.com/hc/en-us/articles/115004481946-Cross-Promotion-Tracking#tracking-cross-promotion-impressions /// /// openStore callback Contains promoted `clickURL` void onOpenStoreLinkGenerated(string link); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerUserInvite.cs.meta ================================================ fileFormatVersion: 2 guid: 5a4cdfa023cb8497b94bb39720052fef MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerValidateAndLog.cs ================================================ namespace AppsFlyerSDK { public interface IAppsFlyerValidateAndLog { /// /// The success callback for validateAndSendInAppPurchase API. /// For Android : the callback will return JSON string. /// For iOS : the callback will return a JSON string from apples verifyReceipt API. /// /// void onValidateAndLogComplete(string result); /// /// The error callback for validateAndSendInAppPurchase API. /// /// A string describing the error. void onValidateAndLogFailure(string error); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerValidateAndLog.cs.meta ================================================ fileFormatVersion: 2 guid: 1a5adb7eab3284dd39a76ec56c06457c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/IAppsFlyerValidateReceipt.cs ================================================ namespace AppsFlyerSDK { public interface IAppsFlyerValidateReceipt { /// /// The success callback for validateAndSendInAppPurchase API. /// For Android : the callback will return "Validate success". /// For iOS : the callback will return a JSON string from apples verifyReceipt API. /// /// void didFinishValidateReceipt(string result); /// /// The error callback for validateAndSendInAppPurchase API. /// /// A string describing the error. void didFinishValidateReceiptWithError(string error); } } ================================================ FILE: Assets/AppsFlyer/IAppsFlyerValidateReceipt.cs.meta ================================================ fileFormatVersion: 2 guid: 6385b1d184efa400a98515735e1f17bc MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/Info.plist ================================================ BuildMachineOSBuild 20G417 CFBundleDevelopmentRegion en CFBundleExecutable AppsFlyerBundle CFBundleIdentifier com.appsflyer.support.two.AppsFlyerBundle CFBundleInfoDictionaryVersion 6.0 CFBundleName AppsFlyerBundle CFBundlePackageType BNDL CFBundleShortVersionString 1.0 CFBundleSupportedPlatforms MacOSX CFBundleVersion 1 DTCompiler com.apple.compilers.llvm.clang.1_0 DTPlatformBuild 13A1030d DTPlatformName macosx DTPlatformVersion 12.0 DTSDKBuild 21A344 DTSDKName macosx12.0 DTXcode 1310 DTXcodeBuild 13A1030d LSMinimumSystemVersion 11.6 NSHumanReadableCopyright ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/Info.plist.meta ================================================ fileFormatVersion: 2 guid: 58a86b0b376564c06bf8ce29e1dbfb04 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/MacOS/AppsFlyerBundle.meta ================================================ fileFormatVersion: 2 guid: 0889edee891d84a8eb0b7cc87071b91e DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/MacOS.meta ================================================ fileFormatVersion: 2 guid: 218a2e7ff5a4c461981bc41f7d7bfeba folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/_CodeSignature/CodeResources ================================================ files files2 rules ^Resources/ ^Resources/.*\.lproj/ optional weight 1000 ^Resources/.*\.lproj/locversion.plist$ omit weight 1100 ^Resources/Base\.lproj/ weight 1010 ^version.plist$ rules2 .*\.dSYM($|/) weight 11 ^(.*/)?\.DS_Store$ omit weight 2000 ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ nested weight 10 ^.* ^Info\.plist$ omit weight 20 ^PkgInfo$ omit weight 20 ^Resources/ weight 20 ^Resources/.*\.lproj/ optional weight 1000 ^Resources/.*\.lproj/locversion.plist$ omit weight 1100 ^Resources/Base\.lproj/ weight 1010 ^[^/]+$ nested weight 10 ^embedded\.provisionprofile$ weight 20 ^version\.plist$ weight 20 ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/_CodeSignature/CodeResources.meta ================================================ fileFormatVersion: 2 guid: 724b52b308e9a4a6d889d7bf3945a2ca DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents/_CodeSignature.meta ================================================ fileFormatVersion: 2 guid: 16068f30788004029bd487756623799b folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle/Contents.meta ================================================ fileFormatVersion: 2 guid: 28175da64865f4e398b3b9ddfbe97b24 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle.meta ================================================ fileFormatVersion: 2 guid: 682114f7790724ab3b9410e89bbc076c folderAsset: yes PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 1 settings: DefaultValueInitialized: true - first: Standalone: OSXUniversal second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Mac.meta ================================================ fileFormatVersion: 2 guid: 96a328019e42349aabc478b546b8605e folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityStoreKit2Bridge.swift ================================================ import Foundation import StoreKit #if canImport(PurchaseConnector) import PurchaseConnector @available(iOS 15.0, *) @objc public class AFUnityStoreKit2Bridge: NSObject { @objc public static func fetchAFSDKTransactionSK2(withTransactionId transactionId: String, completion: @escaping (AFSDKTransactionSK2?) -> Void) { guard let transactionIdUInt64 = UInt64(transactionId) else { print("Invalid transaction ID format.") completion(nil) return } Task { for await result in StoreKit.Transaction.all { if case .verified(let transaction) = result, transaction.id == transactionIdUInt64 { let afTransaction = AFSDKTransactionSK2(transaction: transaction) DispatchQueue.main.async { completion(afTransaction) } return } } DispatchQueue.main.async { completion(nil) } } } @objc public static func extractSK2ProductInfo(_ products: [AFSDKProductSK2]) -> NSArray { var result: [[String: Any]] = [] for product in products { if let swiftProduct = Mirror(reflecting: product).children.first(where: { $0.label == "product" })?.value { let productId = (swiftProduct as? NSObject)?.value(forKey: "id") as? String ?? "" let title = (swiftProduct as? NSObject)?.value(forKey: "displayName") as? String ?? "" let desc = (swiftProduct as? NSObject)?.value(forKey: "description") as? String ?? "" let price = (swiftProduct as? NSObject)?.value(forKey: "price") as? NSDecimalNumber ?? 0 result.append([ "productIdentifier": productId, "localizedTitle": title, "localizedDescription": desc, "price": price ]) } } return result as NSArray } @objc public static func extractSK2TransactionInfo(_ transactions: [AFSDKTransactionSK2]) -> NSArray { var result: [[String: Any]] = [] for txn in transactions { guard let mirrorChild = Mirror(reflecting: txn).children.first(where: { $0.label == "transaction" }), let swiftTxn = mirrorChild.value as? StoreKit.Transaction else { continue } let transactionId = "\(swiftTxn.id)" let date = NSNumber(value: swiftTxn.purchaseDate.timeIntervalSince1970) result.append([ "transactionIdentifier": transactionId, "transactionState": "verified", // or skip this line "transactionDate": date ]) } return result as NSArray } } #endif ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityStoreKit2Bridge.swift.meta ================================================ fileFormatVersion: 2 guid: 5652805602a6b4273a6e527b00aea272 ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityUtils.h ================================================ // // AFUnityUtils.h // // Created by Andrii H. and Dmitry O. on 16 Oct 2023 // #if __has_include() #import #else #import "AppsFlyerLib.h" #endif static NSString* stringFromChar(const char *str); static NSDictionary* dictionaryFromJson(const char *jsonString); static const char* stringFromdictionary(NSDictionary* dictionary); static NSArray *NSArrayFromCArray(int length, const char **arr); static char* getCString(const char* string); static AppsFlyerLinkGenerator* generatorFromDictionary(NSDictionary* dictionary, AppsFlyerLinkGenerator* generator); static EmailCryptType emailCryptTypeFromInt(int emailCryptTypeInt); static AppsFlyerAdRevenueMediationNetworkType mediationNetworkTypeFromInt(int mediationNetwork); static NSNumber *intFromNullableBool(const char *cStr); static NSString* stringFromDeepLinkResultStatus(AFSDKDeepLinkResultStatus deepLinkResult); static NSString* stringFromDeepLinkResultError(AppsFlyerDeepLinkResult *result); ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityUtils.h.meta ================================================ fileFormatVersion: 2 guid: 4b0609ff467554f2088aee1c52bf54a2 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityUtils.mm ================================================ // // AFUnityUtils.mm // Unity-iPhone // // Created by Jonathan Wesfield on 24/07/2019. // #import "AFUnityUtils.h" static NSString* stringFromChar(const char *str) { return str ? [NSString stringWithUTF8String:str] : nil; } static NSDictionary* dictionaryFromJson(const char *jsonString) { if(jsonString){ NSData *jsonData = [[NSData alloc] initWithBytes:jsonString length:strlen(jsonString)]; NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil]; return dictionary; } return nil; } static const char* stringFromdictionary(NSDictionary* dictionary) { if(dictionary){ NSError * err; NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&err]; NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; return [myString UTF8String]; } return nil; } static NSDictionary* dictionaryFromNSError(NSError* error) { if(error){ NSMutableDictionary *errorDictionary = [NSMutableDictionary dictionary]; errorDictionary[@"code"] = @(error.code); errorDictionary[@"localizedDescription"] = error.localizedDescription ?: @""; // Include userInfo fields for enhanced error reporting (iOS SDK 6.17.8+) if (error.userInfo[@"error_code"]) { errorDictionary[@"error_code"] = error.userInfo[@"error_code"]; } if (error.userInfo[@"error_message"]) { errorDictionary[@"error_message"] = error.userInfo[@"error_message"]; } if (error.userInfo[@"invalid_fields"]) { errorDictionary[@"invalid_fields"] = error.userInfo[@"invalid_fields"]; } return errorDictionary; } return nil; } static NSArray *NSArrayFromCArray(int length, const char **arr) { NSMutableArray *res = [[NSMutableArray alloc] init]; for(int i = 0; i < length; i++) { if (arr[i]) { [res addObject:[NSString stringWithUTF8String:arr[i]]]; } } return res; } static char* getCString(const char* string){ if (string == NULL){ return NULL; } char* res = (char*)malloc(strlen(string) + 1); strcpy(res, string); return res; } static AppsFlyerLinkGenerator* generatorFromDictionary(NSDictionary* dictionary, AppsFlyerLinkGenerator* generator) { NSArray* generatorKeys = @[@"channel", @"customerID", @"campaign", @"referrerName", @"referrerImageUrl", @"deeplinkPath", @"baseDeeplink", @"brandDomain"]; NSMutableDictionary* mutableDictionary = [dictionary mutableCopy]; [generator setChannel:[dictionary objectForKey: @"channel"]]; [generator setReferrerCustomerId:[dictionary objectForKey: @"customerID"]]; [generator setCampaign:[dictionary objectForKey: @"campaign"]]; [generator setReferrerName:[dictionary objectForKey: @"referrerName"]]; [generator setReferrerImageURL:[dictionary objectForKey: @"referrerImageUrl"]]; [generator setDeeplinkPath:[dictionary objectForKey: @"deeplinkPath"]]; [generator setBaseDeeplink:[dictionary objectForKey: @"baseDeeplink"]]; [generator setBrandDomain:[dictionary objectForKey: @"brandDomain"]]; [mutableDictionary removeObjectsForKeys:generatorKeys]; [generator addParameters:mutableDictionary]; return generator; } static EmailCryptType emailCryptTypeFromInt(int emailCryptTypeInt){ EmailCryptType emailCryptType; switch (emailCryptTypeInt){ case 1: emailCryptType = EmailCryptTypeSHA256; break; default: emailCryptType = EmailCryptTypeNone; break; } return emailCryptType; } static NSNumber *intFromNullableBool(const char *cStr) { if (!cStr) return nil; NSString *str = [NSString stringWithUTF8String:cStr]; if ([str caseInsensitiveCompare:@"true"] == NSOrderedSame) { return @YES; } else if ([str caseInsensitiveCompare:@"false"] == NSOrderedSame) { return @NO; } return nil; } static AppsFlyerAdRevenueMediationNetworkType mediationNetworkTypeFromInt(int mediationNetworkInt){ AppsFlyerAdRevenueMediationNetworkType mediationNetworkType; switch (mediationNetworkInt){ case 1: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeGoogleAdMob; break; case 2: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeIronSource; break; case 3: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeApplovinMax; break; case 4: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeFyber; break; case 5: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeAppodeal; break; case 6: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeAdmost; break; case 7: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeTopon; break; case 8: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeTradplus; break; case 9: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeYandex; break; case 10: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeChartBoost; break; case 11: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeUnity; break; case 12: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeToponPte; break; case 13: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeCustom; break; case 14: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeDirectMonetization; break; default: mediationNetworkType = AppsFlyerAdRevenueMediationNetworkTypeCustom; break; } return mediationNetworkType; } static NSString* stringFromDeepLinkResultStatus(AFSDKDeepLinkResultStatus deepLinkResult){ NSString* result; switch (deepLinkResult){ case AFSDKDeepLinkResultStatusFound: result = @"FOUND"; break; case AFSDKDeepLinkResultStatusFailure: result = @"ERROR"; break; case AFSDKDeepLinkResultStatusNotFound: result = @"NOT_FOUND"; break; default: result = @"ERROR"; break; } return result; } static NSString* stringFromDeepLinkResultError(AppsFlyerDeepLinkResult *result){ NSString* res; if (result && result.error){ if ([[result.error userInfo][NSUnderlyingErrorKey] code] == -1001) { res = @"TIMEOUT"; } else if ([[result.error userInfo][NSUnderlyingErrorKey] code] == -1009) { res = @"NETWORK"; } } res = @"UNKNOWN"; return res; } ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AFUnityUtils.mm.meta ================================================ fileFormatVersion: 2 guid: 18a03931864e84d86bedcc99c440e060 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: iPhone: iOS second: enabled: 1 settings: {} - first: tvOS: tvOS second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyer+AppController.m ================================================ // // AppsFlyer+AppController.m // Unity-iPhone // // Created by Jonathan Wesfield on 24/07/2019. // #import #import "UnityAppController.h" #import "AppsFlyeriOSWrapper.h" #if __has_include() #import #else #import "AppsFlyerLib.h" #endif @implementation UnityAppController (AppsFlyerSwizzledAppController) static BOOL didEnteredBackGround __unused; static IMP __original_applicationDidBecomeActive_Imp __unused; static IMP __original_applicationDidEnterBackground_Imp __unused; static IMP __original_didReceiveRemoteNotification_Imp __unused; static IMP __original_continueUserActivity_Imp __unused; static IMP __original_openUrl_Imp __unused; + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ #if !AFSDK_SHOULD_SWIZZLE id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"]; BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO; if(shouldSwizzle){ Method method1 = class_getInstanceMethod([self class], @selector(applicationDidBecomeActive:)); __original_applicationDidBecomeActive_Imp = method_setImplementation(method1, (IMP)__swizzled_applicationDidBecomeActive); Method method2 = class_getInstanceMethod([self class], @selector(applicationDidEnterBackground:)); __original_applicationDidEnterBackground_Imp = method_setImplementation(method2, (IMP)__swizzled_applicationDidEnterBackground); Method method3 = class_getInstanceMethod([self class], @selector(didReceiveRemoteNotification:)); __original_didReceiveRemoteNotification_Imp = method_setImplementation(method3, (IMP)__swizzled_didReceiveRemoteNotification); Method method4 = class_getInstanceMethod([self class], @selector(application:openURL:options:)); __original_openUrl_Imp = method_setImplementation(method4, (IMP)__swizzled_openURL); if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } [self swizzleContinueUserActivity:[self class]]; } #elif AFSDK_SHOULD_SWIZZLE Method method1 = class_getInstanceMethod([self class], @selector(applicationDidBecomeActive:)); __original_applicationDidBecomeActive_Imp = method_setImplementation(method1, (IMP)__swizzled_applicationDidBecomeActive); Method method2 = class_getInstanceMethod([self class], @selector(applicationDidEnterBackground:)); __original_applicationDidEnterBackground_Imp = method_setImplementation(method2, (IMP)__swizzled_applicationDidEnterBackground); Method method3 = class_getInstanceMethod([self class], @selector(didReceiveRemoteNotification:)); __original_didReceiveRemoteNotification_Imp = method_setImplementation(method3, (IMP)__swizzled_didReceiveRemoteNotification); Method method4 = class_getInstanceMethod([self class], @selector(application:openURL:options:)); __original_openUrl_Imp = method_setImplementation(method4, (IMP)__swizzled_openURL); if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } [self swizzleContinueUserActivity:[self class]]; #endif }); } +(void)swizzleContinueUserActivity:(Class)class { SEL originalSelector = @selector(application:continueUserActivity:restorationHandler:); Method defaultMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, @selector(__swizzled_continueUserActivity)); BOOL isMethodExists = !class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (isMethodExists) { __original_continueUserActivity_Imp = method_setImplementation(defaultMethod, (IMP)__swizzled_continueUserActivity); } else { class_replaceMethod(class, originalSelector, (IMP)__swizzled_continueUserActivity, method_getTypeEncoding(swizzledMethod)); } } BOOL __swizzled_continueUserActivity(id self, SEL _cmd, UIApplication* application, NSUserActivity* userActivity, void (^restorationHandler)(NSArray*)) { NSLog(@"swizzled continueUserActivity"); [[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler]; if(__original_continueUserActivity_Imp){ return ((BOOL(*)(id, SEL, UIApplication*, NSUserActivity*, void (^)(NSArray*)))__original_continueUserActivity_Imp)(self, _cmd, application, userActivity, NULL); } return YES; } void __swizzled_applicationDidBecomeActive(id self, SEL _cmd, UIApplication* launchOptions) { NSLog(@"swizzled applicationDidBecomeActive"); [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate]; if(didEnteredBackGround && AppsFlyeriOSWarpper.didCallStart == YES){ [[AppsFlyerLib shared] start]; } if(__original_applicationDidBecomeActive_Imp){ ((void(*)(id,SEL, UIApplication*))__original_applicationDidBecomeActive_Imp)(self, _cmd, launchOptions); } } void __swizzled_applicationDidEnterBackground(id self, SEL _cmd, UIApplication* application) { NSLog(@"swizzled applicationDidEnterBackground"); didEnteredBackGround = YES; if(__original_applicationDidEnterBackground_Imp){ ((void(*)(id,SEL, UIApplication*))__original_applicationDidEnterBackground_Imp)(self, _cmd, application); } } BOOL __swizzled_didReceiveRemoteNotification(id self, SEL _cmd, UIApplication* application, NSDictionary* userInfo,void (^UIBackgroundFetchResult)(void) ) { NSLog(@"swizzled didReceiveRemoteNotification"); [[AppsFlyerLib shared] handlePushNotification:userInfo]; if(__original_didReceiveRemoteNotification_Imp){ return ((BOOL(*)(id, SEL, UIApplication*, NSDictionary*, int(UIBackgroundFetchResult)))__original_didReceiveRemoteNotification_Imp)(self, _cmd, application, userInfo, nil); } return YES; } BOOL __swizzled_openURL(id self, SEL _cmd, UIApplication* application, NSURL* url, NSDictionary * options) { NSLog(@"swizzled openURL"); [[AppsFlyerAttribution shared] handleOpenUrl:url options:options]; if(__original_openUrl_Imp){ return ((BOOL(*)(id, SEL, UIApplication*, NSURL*, NSDictionary*))__original_openUrl_Imp)(self, _cmd, application, url, options); } return NO; } @end ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyer+AppController.m.meta ================================================ fileFormatVersion: 2 guid: 6ae9e1f7daef2427588fab2fbf8d35d5 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: iPhone: iOS second: enabled: 1 settings: {} - first: tvOS: tvOS second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAppController.mm ================================================ // // AppsFlyerAppController.mm // Unity-iPhone // // Created by Jonathan Wesfield on 30/07/2019. // #import #import "UnityAppController.h" #import "AppDelegateListener.h" #import "AppsFlyeriOSWrapper.h" #if __has_include() #import #else #import "AppsFlyerLib.h" #endif #import /** Note if you would like to use method swizzeling see AppsFlyer+AppController.m If you are using swizzeling then comment out the method that is being swizzeled in AppsFlyerAppController.mm Only use swizzeling if there are conflicts with other plugins that needs to be resolved. */ @interface AppsFlyerAppController : UnityAppController { BOOL didEnteredBackGround; } @end @implementation AppsFlyerAppController - (instancetype)init { self = [super init]; if (self) { id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"]; BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO; if(!shouldSwizzle){ UnityRegisterAppDelegateListener(self); } } return self; } - (void)didFinishLaunching:(NSNotification*)notification { NSLog(@"got didFinishLaunching = %@",notification.userInfo); if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate]; if (notification.userInfo[@"url"]) { [self onOpenURL:notification]; } } -(void)didBecomeActive:(NSNotification*)notification { NSLog(@"got didBecomeActive(out) = %@", notification.userInfo); if (didEnteredBackGround == YES && AppsFlyeriOSWarpper.didCallStart == YES) { [[AppsFlyerLib shared] start]; didEnteredBackGround = NO; } } - (void)didEnterBackground:(NSNotification*)notification { NSLog(@"got didEnterBackground = %@", notification.userInfo); didEnteredBackGround = YES; } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler { [[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler]; return YES; } - (void)onOpenURL:(NSNotification*)notification { NSLog(@"got onOpenURL = %@", notification.userInfo); NSURL *url = notification.userInfo[@"url"]; NSString *sourceApplication = notification.userInfo[@"sourceApplication"]; if (sourceApplication == nil) { sourceApplication = @""; } if (url != nil) { [[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:nil]; } } - (void)didReceiveRemoteNotification:(NSNotification*)notification { NSLog(@"got didReceiveRemoteNotification = %@", notification.userInfo); [[AppsFlyerLib shared] handlePushNotification:notification.userInfo]; } @end #if !(AFSDK_SHOULD_SWIZZLE) IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController) #endif /** Note if you would not like to use IMPL_APP_CONTROLLER_SUBCLASS you can replace it with the code below. +(void)load { [AppsFlyerAppController plugin]; } // Singleton accessor. + (AppsFlyerAppController *)plugin { static AppsFlyerAppController *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[AppsFlyerAppController alloc] init]; }); return sharedInstance; } **/ ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAppController.mm.meta ================================================ fileFormatVersion: 2 guid: 2d1497a1493b24fecaa58bd3a7b707f9 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: iPhone: iOS second: enabled: 1 settings: {} - first: tvOS: tvOS second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAttribution.h ================================================ // // AppsFlyerAttribution.h // UnityFramework // // Created by Margot Guetta on 11/04/2021. // #ifndef AppsFlyerAttribution_h #define AppsFlyerAttribution_h #endif /* AppsFlyerAttribution_h */ #if __has_include() #import #else #import "AppsFlyerLib.h" #endif @interface AppsFlyerAttribution : NSObject @property NSUserActivity*_Nullable userActivity; @property (nonatomic, copy) void (^ _Nullable restorationHandler)(NSArray *_Nullable ); @property NSURL * _Nullable url; @property NSDictionary * _Nullable options; @property NSString* _Nullable sourceApplication; @property id _Nullable annotation; @property BOOL isBridgeReady; + (AppsFlyerAttribution *_Nullable)shared; - (void) continueUserActivity: (NSUserActivity*_Nullable) userActivity restorationHandler: (void (^_Nullable)(NSArray * _Nullable))restorationHandler; - (void) handleOpenUrl:(NSURL*_Nullable)url options:(NSDictionary*_Nullable) options; - (void) handleOpenUrl: (NSURL *_Nonnull)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation; @end static NSString * _Nullable const AF_BRIDGE_SET = @"bridge is set"; ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAttribution.h.meta ================================================ fileFormatVersion: 2 guid: 8544dc3b3c7bb40d397b2de568df1058 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAttribution.m ================================================ // // NSObject+AppsFlyerAttribution.m // UnityFramework // // Created by Margot Guetta on 11/04/2021. // #import #import "AppsFlyerAttribution.h" @implementation AppsFlyerAttribution + (id)shared { static AppsFlyerAttribution *shared = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [[self alloc] init]; }); return shared; } - (id)init { if (self = [super init]) { self.options = nil; self.restorationHandler = nil; self.url = nil; self.userActivity = nil; self.annotation = nil; self.sourceApplication = nil; self.isBridgeReady = NO; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveBridgeReadyNotification:) name:AF_BRIDGE_SET object:nil]; } return self; } - (void) continueUserActivity: (NSUserActivity*_Nullable) userActivity restorationHandler: (void (^_Nullable)(NSArray * _Nullable))restorationHandler{ if(self.isBridgeReady == YES){ [[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:restorationHandler]; }else{ [AppsFlyerAttribution shared].userActivity = userActivity; [AppsFlyerAttribution shared].restorationHandler = restorationHandler; } } - (void) handleOpenUrl:(NSURL *)url options:(NSDictionary *)options{ if(self.isBridgeReady == YES){ [[AppsFlyerLib shared] handleOpenUrl:url options:options]; }else{ [AppsFlyerAttribution shared].url = url; [AppsFlyerAttribution shared].options = options; } } - (void) handleOpenUrl:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation{ if(self.isBridgeReady == YES){ [[AppsFlyerLib shared] handleOpenURL:url sourceApplication:sourceApplication withAnnotation:annotation]; }else{ [AppsFlyerAttribution shared].url = url; [AppsFlyerAttribution shared].sourceApplication = sourceApplication; [AppsFlyerAttribution shared].annotation = annotation; } } - (void) receiveBridgeReadyNotification:(NSNotification *) notification { NSLog (@"AppsFlyer Debug: handle deep link"); if(self.url && self.sourceApplication){ [[AppsFlyerLib shared] handleOpenURL:self.url sourceApplication:self.sourceApplication withAnnotation:self.annotation]; self.url = nil; self.sourceApplication = nil; self.annotation = nil; }else if(self.options && self.url){ [[AppsFlyerLib shared] handleOpenUrl:self.url options:self.options]; self.options = nil; self.url = nil; }else if(self.userActivity){ [[AppsFlyerLib shared] continueUserActivity:self.userActivity restorationHandler:nil]; self.userActivity = nil; self.restorationHandler = nil; } } @end ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyerAttribution.m.meta ================================================ fileFormatVersion: 2 guid: 1060e47d7b9e2453ba575f0b455b2bf8 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: iPhone: iOS second: enabled: 1 settings: {} - first: tvOS: tvOS second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.h ================================================ // // AppsFlyeriOSWarpper.h // Unity-iPhone // // Created by Jonathan Wesfield on 24/07/2019. // #import "AFUnityUtils.mm" #import "UnityAppController.h" #import "AppsFlyerAttribution.h" #if __has_include() #import #else #import "AppsFlyerLib.h" #endif #if __has_include() #import #else #import "PurchaseConnector.h" #endif #import // Add StoreKit 2 support #if __has_include() #import #endif @interface AppsFlyeriOSWarpper : NSObject + (BOOL) didCallStart; + (void) setDidCallStart:(BOOL)val; // Add StoreKit 2 methods - (void)setStoreKitVersion:(int)storeKitVersion; - (void)logConsumableTransaction:(id)transaction; @end static AppsFlyeriOSWarpper *_AppsFlyerdelegate; static const int kPushNotificationSize = 32; static NSString* ConversionDataCallbackObject = @"AppsFlyerObject"; static const char* VALIDATE_CALLBACK = "didFinishValidateReceipt"; static const char* VALIDATE_ERROR_CALLBACK = "didFinishValidateReceiptWithError"; static const char* GCD_CALLBACK = "onConversionDataSuccess"; static const char* GCD_ERROR_CALLBACK = "onConversionDataFail"; static const char* OAOA_CALLBACK = "onAppOpenAttribution"; static const char* OAOA_ERROR_CALLBACK = "onAppOpenAttributionFailure"; static const char* GENERATE_LINK_CALLBACK = "onInviteLinkGenerated"; static const char* OPEN_STORE_LINK_CALLBACK = "onOpenStoreLinkGenerated"; static const char* START_REQUEST_CALLBACK = "requestResponseReceived"; static const char* IN_APP_RESPONSE_CALLBACK = "inAppResponseReceived"; static const char* ON_DEEPLINKING = "onDeepLinking"; static const char* VALIDATE_AND_LOG_V2_CALLBACK = "onValidateAndLogComplete"; static const char* VALIDATE_AND_LOG_V2_ERROR_CALLBACK = "onValidateAndLogFailure"; static NSString* validateObjectName = @""; static NSString* openStoreObjectName = @""; static NSString* generateInviteObjectName = @""; static NSString* validateAndLogObjectName = @""; static NSString* startRequestObjectName = @""; static NSString* inAppRequestObjectName = @""; static NSString* onDeeplinkingObjectName = @""; static const char* PURCHASE_REVENUE_VALIDATION_CALLBACK = "didReceivePurchaseRevenueValidationInfo"; static const char* PURCHASE_REVENUE_ERROR_CALLBACK = "didReceivePurchaseRevenueError"; static NSString* onPurchaseValidationObjectName = @""; ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.h.meta ================================================ fileFormatVersion: 2 guid: 147104b04b5794eaa92b4195cc328e13 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm ================================================ // // AppsFlyeriOSWarpper.mm // Unity-iPhone // // Created by Jonathan Wesfield on 24/07/2019. // #import "AppsFlyeriOSWrapper.h" #import #import #import "UnityFramework/UnityFramework-Swift.h" #if __has_include() #import #elif __has_include("PurchaseConnector-Swift.h") #import "PurchaseConnector-Swift.h" #endif #if __has_include() #import #elif __has_include("UnityFramework-Swift.h") #import "UnityFramework-Swift.h" #endif static void unityCallBack(NSString* objectName, const char* method, const char* msg) { if(objectName){ UnitySendMessage([objectName UTF8String], method, msg); } } extern "C" { const void _startSDK(bool shouldCallback, const char* objectName) { [[AppsFlyerLib shared] setPluginInfoWith: AFSDKPluginUnity pluginVersion:@"6.17.91" additionalParams:nil]; startRequestObjectName = stringFromChar(objectName); AppsFlyeriOSWarpper.didCallStart = YES; [AppsFlyerAttribution shared].isBridgeReady = YES; [[NSNotificationCenter defaultCenter] postNotificationName:AF_BRIDGE_SET object: [AppsFlyerAttribution shared]]; [[AppsFlyerLib shared] startWithCompletionHandler:^(NSDictionary *dictionary, NSError *error) { if(shouldCallback){ if (error) { NSDictionary *callbackDictionary = @{@"statusCode":[NSNumber numberWithLong:[error code]]}; unityCallBack(startRequestObjectName, START_REQUEST_CALLBACK, stringFromdictionary(callbackDictionary)); return; } if (dictionary) { unityCallBack(startRequestObjectName, START_REQUEST_CALLBACK, stringFromdictionary(dictionary)); return; } } }]; } const void _setCustomerUserID (const char* customerUserID) { [[AppsFlyerLib shared] setCustomerUserID:stringFromChar(customerUserID)]; } const void _setAdditionalData (const char* customData) { [[AppsFlyerLib shared] setAdditionalData:dictionaryFromJson(customData)]; } const void _setAppsFlyerDevKey (const char* appsFlyerDevKey) { [AppsFlyerLib shared].appsFlyerDevKey = stringFromChar(appsFlyerDevKey); } const void _setAppleAppID (const char* appleAppID) { [AppsFlyerLib shared].appleAppID = stringFromChar(appleAppID); } const void _setCurrencyCode (const char* currencyCode) { [[AppsFlyerLib shared] setCurrencyCode:stringFromChar(currencyCode)]; } const void _setDisableCollectAppleAdSupport (bool disableAdvertisingIdentifier) { [AppsFlyerLib shared].disableAdvertisingIdentifier = disableAdvertisingIdentifier; } const void _setIsDebug (bool isDebug) { [AppsFlyerLib shared].isDebug = isDebug; } const void _setShouldCollectDeviceName (bool shouldCollectDeviceName) { [AppsFlyerLib shared].shouldCollectDeviceName = shouldCollectDeviceName; } const void _setAppInviteOneLinkID (const char* appInviteOneLinkID) { [[AppsFlyerLib shared] setAppInviteOneLink:stringFromChar(appInviteOneLinkID)]; } const void _setDeepLinkTimeout (long deepLinkTimeout) { [AppsFlyerLib shared].deepLinkTimeout = deepLinkTimeout; } const void _anonymizeUser (bool anonymizeUser) { [AppsFlyerLib shared].anonymizeUser = anonymizeUser; } const void _enableTCFDataCollection (bool shouldCollectTcfData) { [[AppsFlyerLib shared] enableTCFDataCollection:shouldCollectTcfData]; } const void _setConsentData(const char* isUserSubjectToGDPR, const char* hasConsentForDataUsage, const char* hasConsentForAdsPersonalization, const char* hasConsentForAdStorage) { NSNumber *gdpr = intFromNullableBool(isUserSubjectToGDPR); NSNumber *dataUsage = intFromNullableBool(hasConsentForDataUsage); NSNumber *adsPersonalization = intFromNullableBool(hasConsentForAdsPersonalization); NSNumber *adStorage = intFromNullableBool(hasConsentForAdStorage); AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initWithIsUserSubjectToGDPR:gdpr hasConsentForDataUsage:dataUsage hasConsentForAdsPersonalization:adsPersonalization hasConsentForAdStorage:adStorage]; [[AppsFlyerLib shared] setConsentData:consentData]; } const void _logAdRevenue(const char* monetizationNetwork, int mediationNetworkInt, const char* currencyIso4217Code, double eventRevenue, const char* additionalParameters) { AppsFlyerAdRevenueMediationNetworkType mediationNetwork = mediationNetworkTypeFromInt(mediationNetworkInt); NSNumber *number = [NSNumber numberWithDouble:eventRevenue]; AFAdRevenueData *adRevenue = [[AFAdRevenueData alloc] initWithMonetizationNetwork:stringFromChar(monetizationNetwork) mediationNetwork:mediationNetwork currencyIso4217Code:stringFromChar(currencyIso4217Code) eventRevenue:number]; [[AppsFlyerLib shared] logAdRevenue: adRevenue additionalParameters:dictionaryFromJson(additionalParameters)]; } const void _setDisableCollectIAd (bool disableCollectASA) { [AppsFlyerLib shared].disableCollectASA = disableCollectASA; } const void _setUseReceiptValidationSandbox (bool useReceiptValidationSandbox) { [AppsFlyerLib shared].useReceiptValidationSandbox = useReceiptValidationSandbox; } const void _setUseUninstallSandbox (bool useUninstallSandbox) { [AppsFlyerLib shared].useUninstallSandbox = useUninstallSandbox; } const void _setResolveDeepLinkURLs (int length, const char **resolveDeepLinkURLs) { if(length > 0 && resolveDeepLinkURLs) { [[AppsFlyerLib shared] setResolveDeepLinkURLs:NSArrayFromCArray(length, resolveDeepLinkURLs)]; } } const void _setOneLinkCustomDomains (int length, const char **oneLinkCustomDomains) { if(length > 0 && oneLinkCustomDomains) { [[AppsFlyerLib shared] setOneLinkCustomDomains:NSArrayFromCArray(length, oneLinkCustomDomains)]; } } const void _afSendEvent (const char* eventName, const char* eventValues, bool shouldCallback, const char* objectName) { inAppRequestObjectName = stringFromChar(objectName); [[AppsFlyerLib shared] logEventWithEventName:stringFromChar(eventName) eventValues:dictionaryFromJson(eventValues) completionHandler:^(NSDictionary *dictionary, NSError *error) { if(shouldCallback){ if (error) { NSDictionary *callbackDictionary = @{@"statusCode":[NSNumber numberWithLong:[error code]]}; unityCallBack(inAppRequestObjectName, IN_APP_RESPONSE_CALLBACK, stringFromdictionary(callbackDictionary)); return; } if (dictionary) { unityCallBack(inAppRequestObjectName, IN_APP_RESPONSE_CALLBACK, stringFromdictionary(dictionary)); return; } } }]; } const void _recordLocation (double longitude, double latitude) { [[AppsFlyerLib shared] logLocation:longitude latitude:latitude]; } const char* _getAppsFlyerId () { return getCString([[[AppsFlyerLib shared] getAppsFlyerUID] UTF8String]); } const void _registerUninstall (unsigned char* deviceToken) { if(deviceToken){ NSData* tokenData = [NSData dataWithBytes:(const void *)deviceToken length:sizeof(unsigned char)*kPushNotificationSize]; [[AppsFlyerLib shared] registerUninstall:tokenData]; } } const void _handlePushNotification (const char* pushPayload) { [[AppsFlyerLib shared] handlePushNotification:dictionaryFromJson(pushPayload)]; } const char* _getSDKVersion () { return getCString([[[AppsFlyerLib shared] getSDKVersion] UTF8String]); } const void _setHost (const char* host, const char* hostPrefix) { [[AppsFlyerLib shared] setHost:stringFromChar(host) withHostPrefix:stringFromChar(hostPrefix)]; } const void _setMinTimeBetweenSessions (int minTimeBetweenSessions) { [AppsFlyerLib shared].minTimeBetweenSessions = minTimeBetweenSessions; } const void _stopSDK (bool isStopped) { [AppsFlyerLib shared].isStopped = isStopped; } const BOOL _isSDKStopped () { return [AppsFlyerLib shared].isStopped; } const void _handleOpenUrl(const char *url, const char *sourceApplication, const char *annotation) { [[AppsFlyerLib shared] handleOpenURL:[NSURL URLWithString:stringFromChar(url)] sourceApplication:stringFromChar(sourceApplication) withAnnotation:stringFromChar(annotation)]; } const void _recordCrossPromoteImpression (const char* appID, const char* campaign, const char* parameters) { [AppsFlyerCrossPromotionHelper logCrossPromoteImpression:stringFromChar(appID) campaign:stringFromChar(campaign) parameters:dictionaryFromJson(parameters)]; } const void _attributeAndOpenStore (const char* appID, const char* campaign, const char* parameters, const char* objectName) { openStoreObjectName = stringFromChar(objectName); [AppsFlyerCrossPromotionHelper logAndOpenStore:stringFromChar(appID) campaign:stringFromChar(campaign) parameters:dictionaryFromJson(parameters) openStore:^(NSURLSession * _Nonnull urlSession, NSURL * _Nonnull clickURL) { unityCallBack(openStoreObjectName, OPEN_STORE_LINK_CALLBACK, [clickURL.absoluteString UTF8String]); }]; } const void _generateUserInviteLink (const char* parameters, const char* objectName) { generateInviteObjectName = stringFromChar(objectName); [AppsFlyerShareInviteHelper generateInviteUrlWithLinkGenerator:^AppsFlyerLinkGenerator * _Nonnull(AppsFlyerLinkGenerator * _Nonnull generator) { return generatorFromDictionary(dictionaryFromJson(parameters), generator); } completionHandler:^(NSURL * _Nullable url) { unityCallBack(generateInviteObjectName, GENERATE_LINK_CALLBACK, [url.absoluteString UTF8String]); }]; } const void _recordInvite (const char* channel, const char* parameters) { [AppsFlyerShareInviteHelper logInvite:stringFromChar(channel) parameters:dictionaryFromJson(parameters)]; } const void _setUserEmails (int emailCryptTypeInt , int length, const char **userEmails) { if(length > 0 && userEmails) { [[AppsFlyerLib shared] setUserEmails:NSArrayFromCArray(length, userEmails) withCryptType:emailCryptTypeFromInt(emailCryptTypeInt)]; } } const void _setPhoneNumber (const char* phoneNumber) { [[AppsFlyerLib shared] setPhoneNumber:stringFromChar(phoneNumber)]; } const void _setSharingFilterForAllPartners () { [[AppsFlyerLib shared] setSharingFilterForAllPartners]; } const void _setSharingFilter (int length, const char **partners) { if(length > 0 && partners) { [[AppsFlyerLib shared] setSharingFilter:NSArrayFromCArray(length, partners)]; } } const void _setSharingFilterForPartners (int length, const char **partners) { if(length > 0 && partners) { [[AppsFlyerLib shared] setSharingFilterForPartners:NSArrayFromCArray(length, partners)]; } else { [[AppsFlyerLib shared] setSharingFilterForPartners:nil]; } } const void _validateAndSendInAppPurchase (const char* productIdentifier, const char* price, const char* currency, const char* transactionId, const char* additionalParameters, const char* objectName) { validateObjectName = stringFromChar(objectName); [[AppsFlyerLib shared] validateAndLogInAppPurchase:stringFromChar(productIdentifier) price:stringFromChar(price) currency:stringFromChar(currency) transactionId:stringFromChar(transactionId) additionalParameters:dictionaryFromJson(additionalParameters) success:^(NSDictionary *result){ unityCallBack(validateObjectName, VALIDATE_CALLBACK, stringFromdictionary(result)); } failure:^(NSError *error, id response) { if(response && [response isKindOfClass:[NSDictionary class]]) { NSDictionary* value = (NSDictionary*)response; unityCallBack(validateObjectName, VALIDATE_ERROR_CALLBACK, stringFromdictionary(value)); } else { unityCallBack(validateObjectName, VALIDATE_ERROR_CALLBACK, error ? [[error localizedDescription] UTF8String] : "error"); } }]; } const void _validateAndSendInAppPurchaseV2 (const char* product, const char* transactionId, int purchaseType, const char* purchaseAdditionalDetails, const char* objectName) { validateAndLogObjectName = stringFromChar(objectName); AFSDKPurchaseDetails *details = [[AFSDKPurchaseDetails alloc] initWithProductId:stringFromChar(product) transactionId:stringFromChar(transactionId) purchaseType:(AFSDKPurchaseType)purchaseType]; [[AppsFlyerLib shared] validateAndLogInAppPurchase:details purchaseAdditionalDetails:dictionaryFromJson(purchaseAdditionalDetails) completion:^(NSDictionary * _Nullable response, NSError * _Nullable error) { if (error) { unityCallBack(validateAndLogObjectName, VALIDATE_AND_LOG_V2_ERROR_CALLBACK, stringFromdictionary(dictionaryFromNSError(error))); } else { unityCallBack(validateAndLogObjectName, VALIDATE_AND_LOG_V2_CALLBACK, stringFromdictionary(response)); } }]; } const void _getConversionData(const char* objectName) { if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } ConversionDataCallbackObject = stringFromChar(objectName); [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate]; } const void _waitForATTUserAuthorizationWithTimeoutInterval (int timeoutInterval) { [[AppsFlyerLib shared] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval]; } const void _disableSKAdNetwork (bool isDisabled) { [AppsFlyerLib shared].disableSKAdNetwork = isDisabled; } const void _addPushNotificationDeepLinkPath (int length, const char **paths) { if(length > 0 && paths) { [[AppsFlyerLib shared] addPushNotificationDeepLinkPath:NSArrayFromCArray(length, paths)]; } } const void _subscribeForDeepLink (const char* objectName) { onDeeplinkingObjectName = stringFromChar(objectName); if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } [[AppsFlyerLib shared] setDeepLinkDelegate:_AppsFlyerdelegate]; } const void _setCurrentDeviceLanguage(const char* language) { [[AppsFlyerLib shared] setCurrentDeviceLanguage:stringFromChar(language)]; } const void _setPartnerData(const char* partnerId, const char* partnerInfo) { [[AppsFlyerLib shared] setPartnerDataWithPartnerId: stringFromChar(partnerId) partnerInfo:dictionaryFromJson(partnerInfo)]; } const void _disableIDFVCollection(bool isDisabled) { [AppsFlyerLib shared].disableIDFVCollection = isDisabled; } // Purchase connector const void _startObservingTransactions() { [[PurchaseConnector shared] startObservingTransactions]; } const void _stopObservingTransactions() { [[PurchaseConnector shared] stopObservingTransactions]; } const void _setIsSandbox(bool isSandBox) { [[PurchaseConnector shared] setIsSandbox:isSandBox]; } const void _setPurchaseRevenueDelegate() { if (_AppsFlyerdelegate== nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } [[PurchaseConnector shared] setPurchaseRevenueDelegate:_AppsFlyerdelegate]; } const void _setAutoLogPurchaseRevenue(int option) { [[PurchaseConnector shared] setAutoLogPurchaseRevenue:option]; } const void _initPurchaseConnector(const char* objectName) { if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } onPurchaseValidationObjectName = stringFromChar(objectName); } const void _setPurchaseRevenueDataSource(const char* objectName) { if (_AppsFlyerdelegate == nil) { _AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init]; } if (strstr(objectName, "StoreKit2") != NULL) { // Force protocol conformance Protocol *sk2Protocol = @protocol(AppsFlyerPurchaseRevenueDataSourceStoreKit2); class_addProtocol([_AppsFlyerdelegate class], sk2Protocol); if (![_AppsFlyerdelegate conformsToProtocol:@protocol(AppsFlyerPurchaseRevenueDataSourceStoreKit2)]) { NSLog(@"[AppsFlyer] Warning: SK2 protocol not conformed!"); } } [PurchaseConnector shared].purchaseRevenueDataSource = _AppsFlyerdelegate; } const void _setStoreKitVersion(int storeKitVersion) { [[PurchaseConnector shared] setStoreKitVersion:(AFSDKStoreKitVersion)storeKitVersion]; } const void _logConsumableTransaction(const char* transactionId) { if (@available(iOS 15.0, *)) { NSString *transactionIdStr = [NSString stringWithUTF8String:transactionId]; [AFUnityStoreKit2Bridge fetchAFSDKTransactionSK2WithTransactionId:transactionIdStr completion:^(AFSDKTransactionSK2 *afTransaction) { if (afTransaction) { [[PurchaseConnector shared] logConsumableTransaction:afTransaction]; } else { NSLog(@"No AFSDKTransactionSK2 found for id %@", transactionIdStr); } }]; } } #ifdef __cplusplus extern "C" { #endif typedef const char *(*UnityPurchaseCallback)(const char *, const char *); UnityPurchaseCallback UnityPurchasesGetAdditionalParamsCallback = NULL; UnityPurchaseCallback UnityPurchasesGetAdditionalParamsCallbackSK2 = NULL; __attribute__((visibility("default"))) void RegisterUnityPurchaseRevenueParamsCallback(UnityPurchaseCallback callback) { UnityPurchasesGetAdditionalParamsCallback = callback; } __attribute__((visibility("default"))) void RegisterUnityPurchaseRevenueParamsCallbackSK2(UnityPurchaseCallback callback) { UnityPurchasesGetAdditionalParamsCallbackSK2 = callback; } #ifdef __cplusplus } #endif } @implementation AppsFlyeriOSWarpper static BOOL didCallStart; + (BOOL) didCallStart { @synchronized(self) { return didCallStart; } } + (void) setDidCallStart:(BOOL)val { @synchronized(self) { didCallStart = val; } } - (void)onConversionDataSuccess:(NSDictionary *)installData { unityCallBack(ConversionDataCallbackObject, GCD_CALLBACK, stringFromdictionary(installData)); } - (void)onConversionDataFail:(NSError *)error { unityCallBack(ConversionDataCallbackObject, GCD_ERROR_CALLBACK, [[error localizedDescription] UTF8String]); } - (void)onAppOpenAttribution:(NSDictionary *)attributionData { unityCallBack(ConversionDataCallbackObject, OAOA_CALLBACK, stringFromdictionary(attributionData)); } - (void)onAppOpenAttributionFailure:(NSError *)error { unityCallBack(ConversionDataCallbackObject, OAOA_ERROR_CALLBACK, [[error localizedDescription] UTF8String]); } - (void)didResolveDeepLink:(AppsFlyerDeepLinkResult *)result{ NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; [dict setValue:stringFromDeepLinkResultError(result) forKey:@"error"]; [dict setValue:stringFromDeepLinkResultStatus(result.status) forKey:@"status"]; if(result && result.deepLink){ [dict setValue:result.deepLink.description forKey:@"deepLink"]; [dict setValue:@(result.deepLink.isDeferred) forKey:@"is_deferred"]; } unityCallBack(onDeeplinkingObjectName, ON_DEEPLINKING, stringFromdictionary(dict)); } // Purchase Connector - (void)didReceivePurchaseRevenueValidationInfo:(NSDictionary *)validationInfo error:(NSError *)error { if (error != nil) { unityCallBack(onPurchaseValidationObjectName, PURCHASE_REVENUE_ERROR_CALLBACK, [[error localizedDescription] UTF8String]); } else { unityCallBack(onPurchaseValidationObjectName, PURCHASE_REVENUE_VALIDATION_CALLBACK, stringFromdictionary(validationInfo)); } } - (NSDictionary *)purchaseRevenueAdditionalParametersForProducts:(NSSet *)products transactions:(NSSet *)transactions { NSMutableArray *productsArray = [NSMutableArray array]; for (SKProduct *product in products) { [productsArray addObject:@{ @"productIdentifier": product.productIdentifier ?: @"", @"localizedTitle": product.localizedTitle ?: @"", @"localizedDescription": product.localizedDescription ?: @"", @"price": [product.price stringValue] ?: @"" }]; } NSMutableArray *transactionsArray = [NSMutableArray array]; for (SKPaymentTransaction *txn in transactions) { [transactionsArray addObject:@{ @"transactionIdentifier": txn.transactionIdentifier ?: @"", @"transactionState": @(txn.transactionState), @"transactionDate": txn.transactionDate ? [@(txn.transactionDate.timeIntervalSince1970) stringValue] : @"" }]; } NSDictionary *input = @{ @"products": productsArray, @"transactions": transactionsArray }; NSError *error = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:input options:0 error:&error]; if (error || !jsonData) { NSLog(@"[AppsFlyer] Failed to serialize Unity purchase data: %@", error); return @{}; } NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; if (!jsonString || !UnityPurchasesGetAdditionalParamsCallback) { NSLog(@"[AppsFlyer] Unity callback not registered"); return @{}; } const char *resultCStr = UnityPurchasesGetAdditionalParamsCallback([jsonString UTF8String], ""); if (!resultCStr) { NSLog(@"[AppsFlyer] Unity callback returned null"); return @{}; } NSString *resultJson = [NSString stringWithUTF8String:resultCStr]; NSData *resultData = [resultJson dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:resultData options:0 error:&error]; if (error || ![parsedResult isKindOfClass:[NSDictionary class]]) { NSLog(@"[AppsFlyer] Failed to parse Unity response: %@", error); return @{}; } return parsedResult; } #pragma mark - AppsFlyerPurchaseRevenueDataSourceStoreKit2 - (NSDictionary *)purchaseRevenueAdditionalParametersStoreKit2ForProducts:(NSSet *)products transactions:(NSSet *)transactions { if (@available(iOS 15.0, *)) { NSArray *productInfoArray = [AFUnityStoreKit2Bridge extractSK2ProductInfo:[products allObjects]]; NSArray *transactionInfoArray = [AFUnityStoreKit2Bridge extractSK2TransactionInfo:[transactions allObjects]]; NSDictionary *input = @{ @"products": productInfoArray, @"transactions": transactionInfoArray }; if (UnityPurchasesGetAdditionalParamsCallbackSK2) { NSError *error = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:input options:0 error:&error]; if (error || !jsonData) { NSLog(@"[AppsFlyer] Failed to serialize Unity purchase data: %@", error); return @{}; } NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; const char *resultCStr = UnityPurchasesGetAdditionalParamsCallbackSK2([jsonString UTF8String], ""); if (!resultCStr) { NSLog(@"[AppsFlyer] Unity callback returned null"); return @{}; } NSString *resultJson = [NSString stringWithUTF8String:resultCStr]; NSData *resultData = [resultJson dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *parsedResult = [NSJSONSerialization JSONObjectWithData:resultData options:0 error:&error]; if (error || ![parsedResult isKindOfClass:[NSDictionary class]]) { NSLog(@"[AppsFlyer] Failed to parse Unity response: %@", error); return @{}; } return parsedResult; } else { NSLog(@"[AppsFlyer] SK2 - Unity callback is NOT registered"); } } else { NSLog(@"[AppsFlyer] SK2 - iOS version not supported"); } return @{}; } @end ================================================ FILE: Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm.meta ================================================ fileFormatVersion: 2 guid: 2bff3788f3d8747fe9679bd3818e1d76 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: iPhone: iOS second: enabled: 1 settings: {} - first: tvOS: tvOS second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins/iOS.meta ================================================ fileFormatVersion: 2 guid: d8325c12a80ff4323b82e2833a8fc287 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Plugins.meta ================================================ fileFormatVersion: 2 guid: 66297743248ab4e47abdc371a59f1111 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/ProductPurchase.cs ================================================ #nullable enable using System.Collections; using System.Collections.Generic; using System; using UnityEngine; [System.Serializable] public class InAppPurchaseValidationResult : EventArgs { public bool success; public ProductPurchase? productPurchase; public ValidationFailureData? failureData; public string? token; } [System.Serializable] public class ProductPurchase { public string? kind; public string? purchaseTimeMillis; public int purchaseState; public int consumptionState; public string? developerPayload; public string? orderId; public int purchaseType; public int acknowledgementState; public string? purchaseToken; public string? productId; public int quantity; public string? obfuscatedExternalAccountId; public string? obfuscatedExternalProfil; public string? regionCode; } [System.Serializable] public class ValidationFailureData { public int status; public string? description; } [System.Serializable] public class SubscriptionValidationResult { public bool success; public SubscriptionPurchase? subscriptionPurchase; public ValidationFailureData? failureData; public string? token; } [System.Serializable] public class SubscriptionPurchase { public string? acknowledgementState; public CanceledStateContext? canceledStateContext; public ExternalAccountIdentifiers? externalAccountIdentifiers; public string? kind; public string? latestOrderId; public List? lineItems; public string? linkedPurchaseToken; public PausedStateContext? pausedStateContext; public string? regionCode; public string? startTime; public SubscribeWithGoogleInfo? subscribeWithGoogleInfo; public string? subscriptionState; public TestPurchase? testPurchase; } [System.Serializable] public class CanceledStateContext { public DeveloperInitiatedCancellation? developerInitiatedCancellation; public ReplacementCancellation? replacementCancellation; public SystemInitiatedCancellation? systemInitiatedCancellation; public UserInitiatedCancellation? userInitiatedCancellation; } [System.Serializable] public class ExternalAccountIdentifiers { public string? externalAccountId; public string? obfuscatedExternalAccountId; public string? obfuscatedExternalProfileId; } [System.Serializable] public class SubscriptionPurchaseLineItem { public AutoRenewingPlan? autoRenewingPlan; public DeferredItemReplacement? deferredItemReplacement; public string? expiryTime; public OfferDetails? offerDetails; public PrepaidPlan? prepaidPlan; public string? productId; } [System.Serializable] public class PausedStateContext { public string? autoResumeTime; } [System.Serializable] public class SubscribeWithGoogleInfo { public string? emailAddress; public string? familyName; public string? givenName; public string? profileId; public string? profileName; } [System.Serializable] public class TestPurchase{} [System.Serializable] public class DeveloperInitiatedCancellation{} [System.Serializable] public class ReplacementCancellation{} [System.Serializable] public class SystemInitiatedCancellation{} [System.Serializable] public class UserInitiatedCancellation { public CancelSurveyResult? cancelSurveyResult; public string? cancelTime; } [System.Serializable] public class AutoRenewingPlan { public string? autoRenewEnabled; public SubscriptionItemPriceChangeDetails? priceChangeDetails; } [System.Serializable] public class DeferredItemReplacement { public string? productId; } [System.Serializable] public class OfferDetails { public List? offerTags; public string? basePlanId; public string? offerId; } [System.Serializable] public class PrepaidPlan { public string? allowExtendAfterTime; } [System.Serializable] public class CancelSurveyResult { public string? reason; public string? reasonUserInput; } [System.Serializable] public class SubscriptionItemPriceChangeDetails { public string? expectedNewPriceChargeTime; public Money? newPrice; public string? priceChangeMode; public string? priceChangeState; } [System.Serializable] public class Money { public string? currencyCode; public long nanos; public long units; } ================================================ FILE: Assets/AppsFlyer/ProductPurchase.cs.meta ================================================ fileFormatVersion: 2 guid: 9a1435104a69d4c8ebcc6f237cc29a54 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/Castle.Core.dll.meta ================================================ fileFormatVersion: 2 guid: 7b5b4579db85b4cfd8395bfb19aa309e PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 0 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/NSubstitute.dll.meta ================================================ fileFormatVersion: 2 guid: 642cf65ed2573419bab7e7d44fc73181 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 0 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/System.Runtime.CompilerServices.Unsafe.dll.meta ================================================ fileFormatVersion: 2 guid: 3a5ccbd864ba94a9a9ba47895ff14922 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 0 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/System.Threading.Tasks.Extensions.dll.meta ================================================ fileFormatVersion: 2 guid: ee45ae2608f3c44d08fc6e21a94d133f PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 0 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/Tests.asmdef ================================================ { "name": "Tests", "references": [ "UnityEngine.TestRunner", "UnityEditor.TestRunner", "AppsFlyer" ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [ "nunit.framework.dll", "NSubstitute.dll", "Castle.Core.dll", "System.Threading.Tasks.Extensions.dll" ], "autoReferenced": false, "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/AppsFlyer/Tests/Tests.asmdef.meta ================================================ fileFormatVersion: 2 guid: 1f155a0e4c9ab48eeb4b54b2dc0aeba7 AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests/Tests_Suite.cs ================================================ using System.Collections.Generic; using NUnit.Framework; using UnityEngine; using NSubstitute; namespace AppsFlyerSDK.Tests { public class AppsFlyerSDKTests { private IAppsFlyerNativeBridge mock; [SetUp] public void SetUp() { mock = Substitute.For(); AppsFlyer.instance = mock; } #region SDK Initialization [Test] public void StartSDK_ShouldCallBridge() { AppsFlyer.startSDK(); mock.Received().startSDK(Arg.Any(), Arg.Any()); } [Test] public void StopSDK_True_ShouldCallBridge() { AppsFlyer.stopSDK(true); mock.Received().stopSDK(true); } [Test] public void StopSDK_False_ShouldCallBridge() { AppsFlyer.stopSDK(false); mock.Received().stopSDK(false); } [Test] public void IsSDKStopped_ShouldCallBridge() { _ = AppsFlyer.isSDKStopped(); mock.Received().isSDKStopped(); } #endregion #region Event Sending [Test] public void SendEvent_WithParams_ShouldCallBridge() { var eventParams = new Dictionary { { "key", "value" } }; AppsFlyer.sendEvent("testevent", eventParams); mock.Received().sendEvent("testevent", eventParams, false, null); } [Test] public void SendEvent_NullParams_ShouldCallBridge() { AppsFlyer.sendEvent("testevent", null); mock.Received().sendEvent("testevent", null, false, null); } #endregion #region Identity and Configuration [Test] public void SetCustomerUserId_ShouldCallBridge() { AppsFlyer.setCustomerUserId("user123"); mock.Received().setCustomerUserId("user123"); } [Test] public void SetAppInviteOneLinkID_ShouldCallBridge() { AppsFlyer.setAppInviteOneLinkID("2f36"); mock.Received().setAppInviteOneLinkID("2f36"); } [Test] public void SetAdditionalData_ShouldCallBridge() { var customData = new Dictionary { { "test", "test" } }; AppsFlyer.setAdditionalData(customData); mock.Received().setAdditionalData(customData); } [Test] public void SetResolveDeepLinkURLs_ShouldCallBridge() { AppsFlyer.setResolveDeepLinkURLs("url1", "url2"); mock.Received().setResolveDeepLinkURLs("url1", "url2"); } [Test] public void SetCurrencyCode_ShouldCallBridge() { AppsFlyer.setCurrencyCode("USD"); mock.Received().setCurrencyCode("USD"); } [Test] public void SetMinTimeBetweenSessions_ShouldCallBridge() { AppsFlyer.setMinTimeBetweenSessions(3); mock.Received().setMinTimeBetweenSessions(3); } [Test] public void SetHost_ShouldCallBridge() { AppsFlyer.setHost("prefix", "name"); mock.Received().setHost("prefix", "name"); } [Test] public void SetPhoneNumber_ShouldCallBridge() { AppsFlyer.setPhoneNumber("002"); mock.Received().setPhoneNumber("002"); } [Test] [System.Obsolete] public void SetSharingFilterForAllPartners_ShouldCallBridge() { AppsFlyer.setSharingFilterForAllPartners(); mock.Received().setSharingFilterForAllPartners(); } [Test] [System.Obsolete] public void SetSharingFilter_ShouldCallBridge() { AppsFlyer.setSharingFilter("filter1", "filter2"); mock.Received().setSharingFilter("filter1", "filter2"); } [Test] public void SetConsentData_ShouldCallBridge_WhenInstanceIsNotNull() { var consent = new AppsFlyerConsent(true); AppsFlyer.setConsentData(consent); mock.Received().setConsentData(consent); } [Test] public void SetConsentData_ShouldNotThrow_WhenInstanceIsNull() { AppsFlyer.instance = null; var consent = new AppsFlyerConsent(); Assert.DoesNotThrow(() => AppsFlyer.setConsentData(consent)); } #endregion #region Location and Privacy [Test] public void RecordLocation_ShouldCallBridge() { AppsFlyer.recordLocation(1.23, 4.56); mock.Received().recordLocation(1.23, 4.56); } [Test] public void AnonymizeUser_True_ShouldCallBridge() { AppsFlyer.anonymizeUser(true); mock.Received().anonymizeUser(true); } [Test] public void AnonymizeUser_False_ShouldCallBridge() { AppsFlyer.anonymizeUser(false); mock.Received().anonymizeUser(false); } #endregion #region Utility [Test] public void GetAppsFlyerId_ShouldCallBridge() { AppsFlyer.getAppsFlyerId(); mock.Received().getAppsFlyerId(); } [Test] public void GetConversionData_ShouldCallBridge() { AppsFlyer.getConversionData("ObjectName"); mock.Received().getConversionData("ObjectName"); } [Test] public void GenerateUserInviteLink_ShouldCallBridge() { AppsFlyer.generateUserInviteLink(new Dictionary(), new MonoBehaviour()); mock.Received().generateUserInviteLink(Arg.Any>(), Arg.Any()); } #endregion #region Cross Promotion & Store [Test] public void AttributeAndOpenStore_WithParams_ShouldCallBridge() { Dictionary parameters = new Dictionary(); parameters.Add("af_sub1", "val"); parameters.Add("custom_param", "val2"); AppsFlyer.attributeAndOpenStore("appid", "campaign", parameters, new MonoBehaviour()); mock.Received().attributeAndOpenStore("appid", "campaign", parameters, Arg.Any()); } [Test] public void AttributeAndOpenStore_NullParams_ShouldCallBridge() { AppsFlyer.attributeAndOpenStore("appid", "campaign", null, new MonoBehaviour()); mock.Received().attributeAndOpenStore("appid", "campaign", null, Arg.Any()); } [Test] public void RecordCrossPromoteImpression_WithParams_ShouldCallBridge() { Dictionary parameters = new Dictionary(); parameters.Add("af_sub1", "val"); parameters.Add("custom_param", "val2"); AppsFlyer.recordCrossPromoteImpression("appid", "campaign", parameters); mock.Received().recordCrossPromoteImpression("appid", "campaign", parameters); } [Test] public void RecordCrossPromoteImpression_WithoutParams_ShouldCallBridge() { AppsFlyer.recordCrossPromoteImpression("appid", "campaign", null); mock.Received().recordCrossPromoteImpression("appid", "campaign", null); } [Test] public void AddPushNotificationDeepLinkPath_ShouldCallBridge() { AppsFlyer.addPushNotificationDeepLinkPath("path1", "path2"); mock.Received().addPushNotificationDeepLinkPath("path1", "path2"); } #endregion #if UNITY_ANDROID public class AppsFlyerAndroidTests { private IAppsFlyerAndroidBridge mock; [SetUp] public void SetUp() { mock = Substitute.For(); AppsFlyer.instance = mock; } [Test] public void UpdateServerUninstallToken_ShouldCallBridge() => AppsFlyer.updateServerUninstallToken("tokenTest"); [Test] public void SetImeiData_ShouldCallBridge() => AppsFlyer.setImeiData("imei"); [Test] public void SetAndroidIdData_ShouldCallBridge() => AppsFlyer.setAndroidIdData("androidId"); [Test] public void WaitForCustomerUserId_ShouldCallBridge() => AppsFlyer.waitForCustomerUserId(true); [Test] public void SetCustomerIdAndStartSDK_ShouldCallBridge() => AppsFlyer.setCustomerIdAndStartSDK("01234"); [Test] public void GetOutOfStore_ShouldCallBridge() => AppsFlyer.getOutOfStore(); [Test] public void SetOutOfStore_ShouldCallBridge() => AppsFlyer.setOutOfStore("test"); [Test] public void SetCollectAndroidID_ShouldCallBridge() => AppsFlyer.setCollectAndroidID(true); [Test] public void SetCollectIMEI_ShouldCallBridge() => AppsFlyer.setCollectIMEI(true); [Test] public void SetIsUpdate_ShouldCallBridge() => AppsFlyer.setIsUpdate(true); [Test] public void SetPreinstallAttribution_ShouldCallBridge() => AppsFlyer.setPreinstallAttribution("mediaSourceTestt", "campaign", "sideId"); [Test] public void IsPreInstalledApp_ShouldCallBridge() => AppsFlyer.isPreInstalledApp(); [Test] public void GetAttributionId_ShouldCallBridge() => AppsFlyer.getAttributionId(); [Test] public void HandlePushNotifications_ShouldCallBridge() => AppsFlyer.handlePushNotifications(); [Test] public void ValidateAndSendInAppPurchase_ShouldCallBridge() => AppsFlyer.validateAndSendInAppPurchase("ewjkekwjekw", "hewjehwj", "purchaseData", "3.0", "USD", null, null); [Test] public void SetCollectOaid_ShouldCallBridge() => AppsFlyer.setCollectOaid(true); [Test] public void SetDisableAdvertisingIdentifiers_ShouldCallBridge() => AppsFlyer.setDisableAdvertisingIdentifiers(true); [Test] public void SetDisableNetworkData_ShouldCallBridge() => AppsFlyer.setDisableNetworkData(true); } #endif #if UNITY_IOS public class AppsFlyeriOSTests { private IAppsFlyerIOSBridge mock; [SetUp] public void SetUp() { mock = Substitute.For(); AppsFlyer.instance = mock; } [Test] public void DisableCollectAppleAdSupport_True_ShouldCallBridge() => AppsFlyer.setDisableCollectAppleAdSupport(true); [Test] public void DisableCollectAppleAdSupport_False_ShouldCallBridge() => AppsFlyer.setDisableCollectAppleAdSupport(false); [Test, System.Obsolete] public void ShouldCollectDeviceName_True_ShouldCallBridge() => AppsFlyer.setShouldCollectDeviceName(true); [Test, System.Obsolete] public void ShouldCollectDeviceName_False_ShouldCallBridge() => AppsFlyer.setShouldCollectDeviceName(false); [Test] public void DisableCollectIAd_True_ShouldCallBridge() => AppsFlyer.setDisableCollectIAd(true); [Test] public void DisableCollectIAd_False_ShouldCallBridge() => AppsFlyer.setDisableCollectIAd(false); [Test] public void UseReceiptValidationSandbox_True_ShouldCallBridge() => AppsFlyer.setUseReceiptValidationSandbox(true); [Test] public void UseReceiptValidationSandbox_False_ShouldCallBridge() => AppsFlyer.setUseReceiptValidationSandbox(false); [Test] public void UseUninstallSandbox_True_ShouldCallBridge() => AppsFlyer.setUseUninstallSandbox(true); [Test] public void UseUninstallSandbox_False_ShouldCallBridge() => AppsFlyer.setUseUninstallSandbox(false); [Test] public void ValidateAndSendInAppPurchase_ShouldCallBridge() => AppsFlyer.validateAndSendInAppPurchase("3d2", "5.0", "USD", "45", null, null); [Test] public void RegisterUninstall_ShouldCallBridge() { var token = System.Text.Encoding.UTF8.GetBytes("740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad"); AppsFlyer.registerUninstall(token); mock.Received().registerUninstall(token); } [Test] public void HandleOpenUrl_ShouldCallBridge() => AppsFlyer.handleOpenUrl("www.test.com", "appTest", "test"); [Test] public void WaitForATTUserAuthorizationWithTimeoutInterval_ShouldCallBridge() => AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(30); [Test] public void SetCurrentDeviceLanguage_ShouldCallBridge() => AppsFlyer.setCurrentDeviceLanguage("en"); [Test] public void DisableSKAdNetwork_True_ShouldCallBridge() => AppsFlyer.disableSKAdNetwork(true); [Test] public void DisableSKAdNetwork_False_ShouldCallBridge() => AppsFlyer.disableSKAdNetwork(false); } #endif } } ================================================ FILE: Assets/AppsFlyer/Tests/Tests_Suite.cs.meta ================================================ fileFormatVersion: 2 guid: 1b1a24aa01166451d804d7c03c14a3db MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Tests.meta ================================================ fileFormatVersion: 2 guid: 1f19f272c71674582bed1d93925da003 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Windows/AppsFlyerLib.pdb.meta ================================================ fileFormatVersion: 2 guid: d0748deab2f031f42b801afab0837a59 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Windows/AppsFlyerLib.pri.meta ================================================ fileFormatVersion: 2 guid: c621896ec81267f478e98555031271ef DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Windows/AppsFlyerLib.winmd.meta ================================================ fileFormatVersion: 2 guid: 5186898c6f4665f438e46763d4cff3ae PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 0 settings: DefaultValueInitialized: true - first: Windows Store Apps: WindowsStoreApps second: enabled: 1 settings: CPU: AnyCPU - first: XboxOne: XboxOne second: enabled: 1 settings: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Windows/AppsFlyerWindows.cs ================================================ //#define AFSDK_WIN_DEBUG //#define UNITY_WSA_10_0 //#define ENABLE_WINMD_SUPPORT #if UNITY_WSA_10_0 using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.ComponentModel; using UnityEngine; using System.Threading.Tasks; #if ENABLE_WINMD_SUPPORT using AppsFlyerLib; #endif namespace AppsFlyerSDK { public class AppsFlyerWindows { #if ENABLE_WINMD_SUPPORT static private MonoBehaviour _gameObject = null; #endif public static void InitSDK(string devKey, string appId, MonoBehaviour gameObject) { #if ENABLE_WINMD_SUPPORT #if AFSDK_WIN_DEBUG // Remove callstack Application.SetStackTraceLogType(LogType.Log, StackTraceLogType.None); #endif Log("[InitSDK]: devKey: {0}, appId: {1}, gameObject: {2}", devKey, appId, gameObject == null ? "null" : gameObject.ToString()); AppsFlyerTracker tracker = AppsFlyerTracker.GetAppsFlyerTracker(); tracker.devKey = devKey; tracker.appId = appId; // Interface _gameObject = gameObject; #endif } public static string GetAppsFlyerId() { #if ENABLE_WINMD_SUPPORT Log("[GetAppsFlyerId]"); return AppsFlyerTracker.GetAppsFlyerTracker().GetAppsFlyerUID(); #else return ""; #endif } public static void SetCustomerUserId(string customerUserId) { #if ENABLE_WINMD_SUPPORT Log("[SetCustomerUserId] customerUserId: {0}", customerUserId); if (customerUserId.Contains("test_device:")) { string testDeviceId = customerUserId.Substring(12); AppsFlyerTracker.GetAppsFlyerTracker().testDeviceId = testDeviceId; } AppsFlyerTracker.GetAppsFlyerTracker().customerUserId = customerUserId; #endif } public static void Start() { #if ENABLE_WINMD_SUPPORT Log("[Start]"); AppsFlyerTracker.GetAppsFlyerTracker().TrackAppLaunchAsync(Callback); #endif } #if ENABLE_WINMD_SUPPORT public static void Callback(AppsFlyerLib.ServerStatusCode code) { Log("[Callback]: {0}", code.ToString()); AppsFlyerRequestEventArgs eventArgs = new AppsFlyerRequestEventArgs((int)code, code.ToString()); if (_gameObject != null) { var method = _gameObject.GetType().GetMethod("AppsFlyerOnRequestResponse"); if (method != null) { method.Invoke(_gameObject, new object[] { AppsFlyerTracker.GetAppsFlyerTracker(), eventArgs }); } } } #endif public static void LogEvent(string eventName, Dictionary eventValues) { #if ENABLE_WINMD_SUPPORT if (eventValues == null) { eventValues = new Dictionary(); } IDictionary result = new Dictionary(); foreach (KeyValuePair kvp in eventValues) { result.Add(kvp.Key.ToString(), kvp.Value); } Log("[LogEvent]: eventName: {0} result: {1}", eventName, result.ToString()); AppsFlyerTracker tracker = AppsFlyerTracker.GetAppsFlyerTracker(); tracker.TrackEvent(eventName, result); #endif } public static void GetConversionData(string _reserved) { #if ENABLE_WINMD_SUPPORT Task.Run(async () => { AppsFlyerLib.AppsFlyerTracker tracker = AppsFlyerLib.AppsFlyerTracker.GetAppsFlyerTracker(); string conversionData = await tracker.GetConversionDataAsync(); IAppsFlyerConversionData conversionDataHandler = _gameObject as IAppsFlyerConversionData; if (conversionDataHandler != null) { Log("[GetConversionData] Will call `onConversionDataSuccess` with: {0}", conversionData); conversionDataHandler.onConversionDataSuccess(conversionData); } else { Log("[GetConversionData] Object with `IAppsFlyerConversionData` interface not found! Check `InitSDK` implementation"); } // _gameObject.GetType().GetMethod("onConversionDataSuccess").Invoke(_gameObject, new[] { conversionData }); }); #endif } private static void Log(string format, params string[] args) { #if AFSDK_WIN_DEBUG #if ENABLE_WINMD_SUPPORT Debug.Log("AF_UNITY_WSA_10_0" + String.Format(format, args)); #endif #endif } } } #endif ================================================ FILE: Assets/AppsFlyer/Windows/AppsFlyerWindows.cs.meta ================================================ fileFormatVersion: 2 guid: 034d11e52b599954181d7f08c0d89ca8 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/Windows.meta ================================================ fileFormatVersion: 2 guid: 1fbfcb6aeaa7f40e69a0daff450a2450 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer/package.json ================================================ { "name": "appsflyer-unity-plugin", "displayName": "AppsFlyer", "description": "AppsFlyer Unity plugin", "version": "6.17.91", "unity": "2019.4", "license": "MIT" } ================================================ FILE: Assets/AppsFlyer/package.json.meta ================================================ fileFormatVersion: 2 guid: a2b3ab5da7dda473b8791503604647b4 TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/AppsFlyer.meta ================================================ fileFormatVersion: 2 guid: 7863556d88b814e09ba9cfc75a91d655 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: CHANGELOG.md ================================================ # Versions ## v6.17.91 * Update Android unity-wrapper to Billing Library 8 (billing:8.0.0) ## v6.17.90 * Update Android SDK version - 6.17.6 * Update iOS SDK version - 6.17.9 * Update iOS Purchase Connector version - 6.17.9 * Two unity-wrapper variants published: * v6.17.90 — Billing Library 7 (billing:5.2.0) * v6.17.91 — Billing Library 8 (billing:8.0.0) * Update appsflyer_logo ## v6.17.81 * Update Android Purchase Connector version - 2.2.0 ## v6.17.80 * Update Android SDK version - 6.17.5 * Update Android Purchase Connector version - 2.1.2 * Update iOS SDK version - 6.17.8 * Update iOS Purchase Connector version - 6.17.8 ## v6.17.72 * Use Android Purchase Connector version - 2.1.1 ## v6.17.71 * Use Android Purchase Connector version - 2.1.0 ## v6.17.7 * Update iOS SDK version - 6.17.7 * Update iOS Purchase Connector version - 6.17.7 * Update Android Purchase Connector version - 2.2.0 ## v6.17.5 * Update iOS SDK version - 6.17.5 * Update iOS Purchase Connector version - 6.17.5 * Update Android SDK ersion - 6.17.3 * Validate and log API changes. ## v6.17.1 * Purchase Connector support. * Update iOS SDK version - 6.17.1 * Update Android SDK ersion - 6.17.0 ## v6.16.21 * Bug fix - AppsflyerConsent - nonGDPR init ## v6.16.2 * Update iOS SDK version - 6.16.2 * Update Android SDK version - 6.16.2 * Update AppsflyerConsent object and methods + deprecation of old methods ## v6.16.0 * Update iOS SDK version - 6.16.0 * Update Android SDK version - 6.16.0 ## v6.15.3 * Fix logAdRevenue - revenue value will not be rounded ## v6.15.2 * Update iOS SDK version - 6.15.2 ## v6.15.1 * Update iOS SDK version - 6.15.1 * Update Android SDK version - 6.15.0 * Added new iOS and Android API `logAdRevenue` ## v6.14.5 * Update iOS SDK version - 6.14.5 ## v6.14.4 * Update iOS SDK version - 6.14.4 * Update Android SDK version - 6.14.2 ## v6.14.3 * Update iOS SDK version - 6.14.3 * Update Android SDK version - 6.14.0 * Added new version of iOS and Android API `validateAndSendInAppPurchase` (This API is currently in closed beta. Please contact AppsFlyer before using it). * Android Huawei install referrer update - [huawei-install-referrer](https://dev.appsflyer.com/hc/docs/install-android-sdk#huawei-install-referrer) ## v6.14.0 * Update iOS SDK version - 6.14.0 * iOS Privacy Manifest support, [for more information about privacy manifest](https://support.appsflyer.com/hc/en-us/articles/21677433322641-Privacy-Manifest) * Xcode 15.3 bug fix ## v6.13.10 * Update iOS SDK version - 6.13.1 ## v6.13.0 * Update iOS SDK version - 6.13.0 * Update Android SDK version - 6.13.0 * Added new iOS and Android API `setDeepLinkTimeout` * Added new iOS and Android API `enableTCFDataCollection` * Added new iOS and Android API `setConsentData` ## v6.12.22 * Update EDM4U - 1.2.177 * Fix AFUnityUtils import ## v6.12.21 * Update iOS SDK version - 6.12.2 ## v6.12.20 * Update iOS SDK version - 6.12.1 * Update Android SDK version - 6.12.2 * UPM distribution change, [see breaking changes](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/tree/development#------breaking-changes-when-updating-to-61220-). * Added new iOS API `disableIDFVCollection` ## v6.12.10 * Update iOS SDK version - 6.12.0 * Update Android SDK version - 6.12.1 ## v6.10.30 * Update iOS SDK version - 6.10.1 * Update Android SDK version - 6.10.3 ## v6.10.10 * Update iOS SDK version - 6.10.0 * Update Android SDK version - 6.10.1 * Fix NullReferenceException when not using Request Listeners with UWP * Align `setHost`, `disableSKAdNetwork` and `setCurrencyCode` to work if called prior to `initSdk` ## v6.9.4 * Update iOS SDK version - 6.9.1 * Update Android SDK version - 6.9.4 * Fix appsflyer logo on prefab * Fix attributeAndOpenStore issue on Android ## v6.8.5 * Fix setHost API for iOS ## v6.8.4 * Fix package ## v6.8.3 * Fix android issue * Fix in-app callback issue ## v6.8.2 * Fix dependency for strict mode ## v6.8.1 * Update iOS SDK version - 6.8.1 * Update Android SDK version - 6.8.0 * Added new Android API `setDisableNetworkData` ## v6.6.0 * Update iOS SDK version - 6.6.0 * Update Android SDK version - 6.6.0 * add new API setPartnerData * Beta support for MacOS ## v6.5.4 * Update iOS SDK version - 6.5.4 * Update Android SDK version - 6.5.4 ## v6.5.3 * Additional solution for Swizzling using macroprocessor ## v6.5.2 * Update iOS SDK version - 6.5.2 * Update Android SDK version - 6.5.2 ## v6.4.41 * Fix issue with deep linking * `getAppsFlyerUID` support for uwp ## v6.4.4 * Update iOS SDK version - 6.4.4 * Update Android SDK version - 6.4.3 * Fixed issue with deep linking ## v6.4.1 * Update iOS SDK version - 6.4.1 * Update Android SDK version - 6.4.1 * Fixed a bug causing a crash in Unity apps ## v6.4.0 * Update iOS SDK version - 6.4.0 * Update Android SDK version - 6.4.0 * new API `setSharingFilterForPartners` * Deprecated API `setSharingFilterForAllPartners` and `setSharingFilter` * Android Target API updated to 30 ## v6.3.5 * Update iOS SDK version - 6.3.5 * Fix issue with Facebook login * Fix issue with uwp ## v6.3.2 * Update iOS SDK version - 6.3.2 * Update Android SDK version - 6.3.2 * new Android API `setDisableAdvertisingIdentifiers` ## v6.3.1 * Update iOS SDK version - 6.3.1 ## v6.3.0 * Support for UWP * Update iOS SDK version - 6.3.0 * Update Android SDK version - 6.3.0 ## v6.2.63 * fix swizzling DDL ## v6.2.62 * fix setOneLinkCustomDomains API * fix swizzling ## v6.2.61 * Fix android dependency ## v6.2.6 * Update iOS SDK version - 6.2.6 * Update Android SDK version - 6.2.3 ## v6.2.5 * Update iOS SDK version - 6.2.5 * Deprecated setShouldCollectDeviceName * AttributionObject to handle DeepLink ## v6.2.41 * Fix Skad issue ## v6.2.4 * RD-59026 - iOS SDK Version - 6.2.4 ## v6.2.3 * RD-54266 - iOS SDK Version - 6.2.3 ## v6.2.2 * RD-54266 - iOS SDK Version - 6.2.2 * RD-54266 - Android SDK Version - 6.2.0 ## v6.2.0 * RD-55161 - Fixed don't call start before startSDK() (iOS) * RD-55566 - Fix onAppOpenAttribution called from kill for swizziling class (iOS) * RD-45032 - Send reponse code in Purchase Validation Error (iOS) * RD-54266 - iOS SDK Version - 6.2.0 * RD-54266 - Android SDK Version - 6.1.4 ## v6.1.4 * RD-55566 - Fix onAppOpenAttribution called from kill ## v6.1.3 * RD-50954 - Added Unified Deep Linking API * RD-54264 - Added addPushNotificationDeepLinkPath api for iOS & Android * RD-54266 - iOS SDK Version - 6.1.3 * RD-54266 - Android SDK Version - 6.1.3 ## v6.1.0 * iOS SDK Version - 6.1.1 * Android SDK Version - 6.1.0 * Added onRequestResponse and onInAppResponse events. ## v6.0.7 * RD-49435 - ios swizzle options fix * iOS SDK Version - 6.0.7 ## v6.0.6 * RD-48888 - continueUserActivity remove super call * RD-48915 - AppsFlyer+AppController update to lastest version ## v6.0.5 * iOS SDK Version - 6.0.5 ## 6.0.3 * RD-44538 - empty game object fix * Added disableSKAdNetwork api * Added waitForATTUserAuthorizationWithTimeoutInterval api * Update android [installreferrer](https://mvnrepository.com/artifact/com.android.installreferrer/installreferrer) to 2.1 * Android SDK Version - 5.4.3 * iOS SDK Version - 6.0.3 ## 5.4.2 * RD-43178 - added setSharingFilterForAllPartners() api * RD-43178 - added setSharingFilter(params string[] partners) api * RD-42761 - fix validateAndSendInAppPurchase callback on iOS ## 5.4.1 * RD-40404 - add additional params for recordCrossPromoteImpression api (ios & android) * RD-42760 - add setPhoneNumber api (ios & android) * RD-42761 - fix validateAndSendInAppPurchase callback on iOS * Android SDK Version - 5.4.1 * iOS SDK Version - 5.4.1 ## 5.3.1 * RD-39294 - add super call from continueUserActivity * RD-39255 - make IAppsFlyerConversionData public * RD-39254 - add setCollectOaid api (android) * RD-39216 - add handleOpenUrl api (iOS) ## 5.3.0 * Android SDK Version - 5.3.0 * iOS SDK Version. - 5.3.0 * Prefab fix for unity 2018.2.21f ## 5.2.1 * RD-37433 - Fix for 'duplicate symbol '__sendEvent' issue ## 5.2.0 * Android SDK Version - 5.2.0 * iOS SDK Version. - 5.2.0 Changes and fixes: - New plugin with breaking changes. Please see migration doc with details. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 AppsFlyerSDK 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. ================================================ FILE: ProjectSettings/AudioManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!11 &1 AudioManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_Volume: 1 Rolloff Scale: 1 Doppler Factor: 1 Default Speaker Mode: 2 m_SampleRate: 0 m_DSPBufferSize: 1024 m_VirtualVoiceCount: 512 m_RealVoiceCount: 32 m_EnableOutputSuspension: 1 m_SpatializerPlugin: m_AmbisonicDecoderPlugin: m_DisableAudio: 0 m_VirtualizeEffects: 1 m_RequestedDSPBufferSize: 0 ================================================ FILE: ProjectSettings/ClusterInputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!236 &1 ClusterInputManager: m_ObjectHideFlags: 0 m_Inputs: [] ================================================ FILE: ProjectSettings/DynamicsManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!55 &1 PhysicsManager: m_ObjectHideFlags: 0 serializedVersion: 13 m_Gravity: {x: 0, y: -9.81, z: 0} m_DefaultMaterial: {fileID: 0} m_BounceThreshold: 2 m_SleepThreshold: 0.005 m_DefaultContactOffset: 0.01 m_DefaultSolverIterations: 6 m_DefaultSolverVelocityIterations: 1 m_QueriesHitBackfaces: 0 m_QueriesHitTriggers: 1 m_EnableAdaptiveForce: 0 m_ClothInterCollisionDistance: 0.1 m_ClothInterCollisionStiffness: 0.2 m_ContactsGeneration: 1 m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_AutoSimulation: 1 m_AutoSyncTransforms: 0 m_ReuseCollisionCallbacks: 0 m_ClothInterCollisionSettingsToggle: 0 m_ClothGravity: {x: 0, y: -9.81, z: 0} m_ContactPairsMode: 0 m_BroadphaseType: 0 m_WorldBounds: m_Center: {x: 0, y: 0, z: 0} m_Extent: {x: 250, y: 250, z: 250} m_WorldSubdivisions: 8 m_FrictionType: 0 m_EnableEnhancedDeterminism: 0 m_EnableUnifiedHeightmaps: 1 m_SolverType: 0 m_DefaultMaxAngularSpeed: 50 ================================================ FILE: ProjectSettings/EditorBuildSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1045 &1 EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: [] m_configObjects: {} ================================================ FILE: ProjectSettings/EditorSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!159 &1 EditorSettings: m_ObjectHideFlags: 0 serializedVersion: 9 m_ExternalVersionControlSupport: Visible Meta Files m_SerializationMode: 2 m_LineEndingsForNewScripts: 1 m_DefaultBehaviorMode: 0 m_PrefabRegularEnvironment: {fileID: 0} m_PrefabUIEnvironment: {fileID: 0} m_SpritePackerMode: 0 m_SpritePackerPaddingPower: 1 m_EtcTextureCompressorBehavior: 1 m_EtcTextureFastCompressor: 1 m_EtcTextureNormalCompressor: 2 m_EtcTextureBestCompressor: 4 m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp m_ProjectGenerationRootNamespace: m_CollabEditorSettings: inProgressEnabled: 1 m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_AsyncShaderCompilation: 1 m_EnterPlayModeOptionsEnabled: 0 m_EnterPlayModeOptions: 3 m_ShowLightmapResolutionOverlay: 1 m_UseLegacyProbeSampleCount: 0 m_AssetPipelineMode: 1 m_CacheServerMode: 0 m_CacheServerEndpoint: m_CacheServerNamespacePrefix: default m_CacheServerEnableDownload: 1 m_CacheServerEnableUpload: 1 ================================================ FILE: ProjectSettings/GraphicsSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!30 &1 GraphicsSettings: m_ObjectHideFlags: 0 serializedVersion: 13 m_Deferred: m_Mode: 1 m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} m_DeferredReflections: m_Mode: 1 m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} m_ScreenSpaceShadows: m_Mode: 1 m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} m_LegacyDeferred: m_Mode: 1 m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} m_DepthNormals: m_Mode: 1 m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} m_MotionVectors: m_Mode: 1 m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} m_LightHalo: m_Mode: 1 m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} m_LensFlare: m_Mode: 1 m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} m_AlwaysIncludedShaders: - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} m_PreloadedShaders: [] m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_CustomRenderPipeline: {fileID: 0} m_TransparencySortMode: 0 m_TransparencySortAxis: {x: 0, y: 0, z: 1} m_DefaultRenderingPath: 1 m_DefaultMobileRenderingPath: 1 m_TierSettings: [] m_LightmapStripping: 0 m_FogStripping: 0 m_InstancingStripping: 0 m_LightmapKeepPlain: 1 m_LightmapKeepDirCombined: 1 m_LightmapKeepDynamicPlain: 1 m_LightmapKeepDynamicDirCombined: 1 m_LightmapKeepShadowMask: 1 m_LightmapKeepSubtractive: 1 m_FogKeepLinear: 1 m_FogKeepExp: 1 m_FogKeepExp2: 1 m_AlbedoSwatchInfos: [] m_LightsUseLinearIntensity: 0 m_LightsUseColorTemperature: 0 m_LogWhenShaderIsCompiled: 0 m_AllowEnlightenSupportForUpgradedProject: 0 ================================================ FILE: ProjectSettings/InputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!13 &1 InputManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_Axes: - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: left positiveButton: right altNegativeButton: a altPositiveButton: d gravity: 3 dead: 0.001 sensitivity: 3 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: down positiveButton: up altNegativeButton: s altPositiveButton: w gravity: 3 dead: 0.001 sensitivity: 3 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left ctrl altNegativeButton: altPositiveButton: mouse 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left alt altNegativeButton: altPositiveButton: mouse 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left shift altNegativeButton: altPositiveButton: mouse 2 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: space altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse X descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse Y descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Mouse ScrollWheel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 2 joyNum: 0 - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 0 type: 2 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 1 type: 2 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 0 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 1 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 2 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 3 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: return altNegativeButton: altPositiveButton: joystick button 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: enter altNegativeButton: altPositiveButton: space gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Cancel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: escape altNegativeButton: altPositiveButton: joystick button 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 ================================================ FILE: ProjectSettings/MemorySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!387306366 &1 MemorySettings: m_ObjectHideFlags: 0 m_EditorMemorySettings: m_MainAllocatorBlockSize: -1 m_ThreadAllocatorBlockSize: -1 m_MainGfxBlockSize: -1 m_ThreadGfxBlockSize: -1 m_CacheBlockSize: -1 m_TypetreeBlockSize: -1 m_ProfilerBlockSize: -1 m_ProfilerEditorBlockSize: -1 m_BucketAllocatorGranularity: -1 m_BucketAllocatorBucketsCount: -1 m_BucketAllocatorBlockSize: -1 m_BucketAllocatorBlockCount: -1 m_ProfilerBucketAllocatorGranularity: -1 m_ProfilerBucketAllocatorBucketsCount: -1 m_ProfilerBucketAllocatorBlockSize: -1 m_ProfilerBucketAllocatorBlockCount: -1 m_TempAllocatorSizeMain: -1 m_JobTempAllocatorBlockSize: -1 m_BackgroundJobTempAllocatorBlockSize: -1 m_JobTempAllocatorReducedBlockSize: -1 m_TempAllocatorSizeGIBakingWorker: -1 m_TempAllocatorSizeNavMeshWorker: -1 m_TempAllocatorSizeAudioWorker: -1 m_TempAllocatorSizeCloudWorker: -1 m_TempAllocatorSizeGfx: -1 m_TempAllocatorSizeJobWorker: -1 m_TempAllocatorSizeBackgroundWorker: -1 m_TempAllocatorSizePreloadManager: -1 m_PlatformMemorySettings: {} ================================================ FILE: ProjectSettings/MultiplayerManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!655991488 &1 MultiplayerManager: m_ObjectHideFlags: 0 m_EnableMultiplayerRoles: 0 m_StrippingTypes: {} ================================================ FILE: ProjectSettings/NavMeshAreas.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!126 &1 NavMeshProjectSettings: m_ObjectHideFlags: 0 serializedVersion: 2 areas: - name: Walkable cost: 1 - name: Not Walkable cost: 1 - name: Jump cost: 2 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 m_LastAgentTypeID: -887442657 m_Settings: - serializedVersion: 2 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.75 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 accuratePlacement: 0 debug: m_Flags: 0 m_SettingNames: - Humanoid ================================================ FILE: ProjectSettings/PackageManagerSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 61 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_ScopedRegistriesSettingsExpanded: 1 oneTimeWarningShown: 0 m_Registries: - m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 m_UserSelectedRegistryName: m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_ErrorMessage: m_Original: m_Id: m_Name: m_Url: m_Scopes: [] m_IsDefault: 0 m_Modified: 0 m_Name: m_Url: m_Scopes: - m_SelectedScopeIndex: 0 ================================================ FILE: ProjectSettings/Physics2DSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!19 &1 Physics2DSettings: m_ObjectHideFlags: 0 serializedVersion: 4 m_Gravity: {x: 0, y: -9.81} m_DefaultMaterial: {fileID: 0} m_VelocityIterations: 8 m_PositionIterations: 3 m_VelocityThreshold: 1 m_MaxLinearCorrection: 0.2 m_MaxAngularCorrection: 8 m_MaxTranslationSpeed: 100 m_MaxRotationSpeed: 360 m_BaumgarteScale: 0.2 m_BaumgarteTimeOfImpactScale: 0.75 m_TimeToSleep: 0.5 m_LinearSleepTolerance: 0.01 m_AngularSleepTolerance: 2 m_DefaultContactOffset: 0.01 m_JobOptions: serializedVersion: 2 useMultithreading: 0 useConsistencySorting: 0 m_InterpolationPosesPerJob: 100 m_NewContactsPerJob: 30 m_CollideContactsPerJob: 100 m_ClearFlagsPerJob: 200 m_ClearBodyForcesPerJob: 200 m_SyncDiscreteFixturesPerJob: 50 m_SyncContinuousFixturesPerJob: 50 m_FindNearestContactsPerJob: 100 m_UpdateTriggerContactsPerJob: 100 m_IslandSolverCostThreshold: 100 m_IslandSolverBodyCostScale: 1 m_IslandSolverContactCostScale: 10 m_IslandSolverJointCostScale: 10 m_IslandSolverBodiesPerJob: 50 m_IslandSolverContactsPerJob: 50 m_AutoSimulation: 1 m_QueriesHitTriggers: 1 m_QueriesStartInColliders: 1 m_CallbacksOnDisable: 1 m_ReuseCollisionCallbacks: 0 m_AutoSyncTransforms: 0 m_AlwaysShowColliders: 0 m_ShowColliderSleep: 1 m_ShowColliderContacts: 0 m_ShowColliderAABB: 0 m_ContactArrowScale: 0.2 m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ================================================ FILE: ProjectSettings/PresetManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1386491679 &1 PresetManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_DefaultPresets: {} ================================================ FILE: ProjectSettings/ProjectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 serializedVersion: 23 productGUID: 5190ae4a335a1487590079a7f228b806 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 AndroidEnableSustainedPerformanceMode: 0 defaultScreenOrientation: 4 targetDevice: 2 useOnDemandResources: 0 accelerometerFrequency: 60 companyName: DefaultCompany productName: appsflyer-unity-plugin defaultCursor: {fileID: 0} cursorHotspot: {x: 0, y: 0} m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} m_ShowUnitySplashScreen: 1 m_ShowUnitySplashLogo: 1 m_SplashScreenOverlayOpacity: 1 m_SplashScreenAnimation: 1 m_SplashScreenLogoStyle: 1 m_SplashScreenDrawMode: 0 m_SplashScreenBackgroundAnimationZoom: 1 m_SplashScreenLogoAnimationZoom: 1 m_SplashScreenBackgroundLandscapeAspect: 1 m_SplashScreenBackgroundPortraitAspect: 1 m_SplashScreenBackgroundLandscapeUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenBackgroundPortraitUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1024 defaultScreenHeight: 768 defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 m_StackTraceTypes: 010000000100000001000000010000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 iosAllowHTTPDownload: 1 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1 allowedAutorotateToLandscapeRight: 1 allowedAutorotateToLandscapeLeft: 1 useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 0 androidBlitType: 0 androidResizableWindow: 0 androidDefaultWindowWidth: 1920 androidDefaultWindowHeight: 1080 androidMinimumWindowWidth: 400 androidMinimumWindowHeight: 300 androidFullscreenMode: 1 defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 captureSingleScreen: 0 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 usePlayerLog: 1 bakeCollisionMeshes: 0 forceSingleInstance: 0 useFlipModelSwapchain: 1 resizableWindow: 0 useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 0 xboxPIXTextureCapture: 0 xboxEnableAvatar: 0 xboxEnableKinect: 0 xboxEnableKinectAutoTracking: 0 xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 fullscreenMode: 1 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 xboxOneMonoLoggingLevel: 0 xboxOneLoggingLevel: 1 xboxOneDisableEsram: 0 xboxOneEnableTypeOptimization: 0 xboxOnePresentImmediateThreshold: 0 switchQueueCommandMemory: 1048576 switchQueueControlMemory: 16384 switchQueueComputeMemory: 262144 switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 switchNVNMaxPublicTextureIDCount: 0 switchNVNMaxPublicSamplerIDCount: 0 stadiaPresentMode: 0 stadiaTargetFramerate: 0 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 vulkanEnableLateAcquireNextImage: 0 vulkanEnableCommandBufferRecycling: 1 m_SupportedAspectRatios: 4:3: 1 5:4: 1 16:10: 1 16:9: 1 Others: 1 bundleVersion: 1.0 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 useHDRDisplay: 0 D3DHDRBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: {} buildNumber: Standalone: 0 iPhone: 0 tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 AndroidMinSdkVersion: 22 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 ForceInternetPermission: 0 ForceSDCardPermission: 0 CreateWallpaper: 0 APKExpansionFiles: 0 keepLoadedShadersAlive: 0 StripUnusedMeshComponents: 0 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSTargetOSVersionString: 11.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 tvOSTargetOSVersionString: 11.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 uIStatusBarHidden: 1 uIExitOnSuspend: 0 uIStatusBarStyle: 0 appleTVSplashScreen: {fileID: 0} appleTVSplashScreen2x: {fileID: 0} tvOSSmallIconLayers: [] tvOSSmallIconLayers2x: [] tvOSLargeIconLayers: [] tvOSLargeIconLayers2x: [] tvOSTopShelfImageLayers: [] tvOSTopShelfImageLayers2x: [] tvOSTopShelfImageWideLayers: [] tvOSTopShelfImageWideLayers2x: [] iOSLaunchScreenType: 0 iOSLaunchScreenPortrait: {fileID: 0} iOSLaunchScreenLandscape: {fileID: 0} iOSLaunchScreenBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreenFillPct: 100 iOSLaunchScreenSize: 100 iOSLaunchScreenCustomXibPath: iOSLaunchScreeniPadType: 0 iOSLaunchScreeniPadImage: {fileID: 0} iOSLaunchScreeniPadBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 iOSLaunchScreeniPadCustomXibPath: iOSLaunchScreenCustomStoryboardPath: iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 metalAPIValidation: 1 iOSRenderExtraFrameOnPause: 0 iosCopyPluginsCodeInsteadOfSymlink: 0 appleDeveloperTeamID: iOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0 appleEnableAutomaticSigning: 0 iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 appleEnableProMotion: 0 shaderPrecisionModel: 0 clonedFromGUID: 00000000000000000000000000000000 templatePackageId: templateDefaultScene: useCustomMainManifest: 0 useCustomLauncherManifest: 0 useCustomMainGradleTemplate: 0 useCustomLauncherGradleManifest: 0 useCustomBaseGradleTemplate: 0 useCustomGradlePropertiesTemplate: 0 useCustomProguardFile: 0 AndroidTargetArchitectures: 1 AndroidTargetDevices: 0 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} AndroidKeystoreName: AndroidKeyaliasName: AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 androidUseCustomKeystore: 0 m_AndroidBanners: - width: 320 height: 180 banner: {fileID: 0} androidGamepadSupportLevel: 0 chromeosInputEmulation: 1 AndroidMinifyWithR8: 0 AndroidMinifyRelease: 0 AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 150 m_BuildTargetIcons: [] m_BuildTargetPlatformIcons: - m_BuildTarget: iPhone m_Icons: - m_Textures: [] m_Width: 180 m_Height: 180 m_Kind: 0 m_SubKind: iPhone - m_Textures: [] m_Width: 120 m_Height: 120 m_Kind: 0 m_SubKind: iPhone - m_Textures: [] m_Width: 167 m_Height: 167 m_Kind: 0 m_SubKind: iPad - m_Textures: [] m_Width: 152 m_Height: 152 m_Kind: 0 m_SubKind: iPad - m_Textures: [] m_Width: 76 m_Height: 76 m_Kind: 0 m_SubKind: iPad - m_Textures: [] m_Width: 120 m_Height: 120 m_Kind: 3 m_SubKind: iPhone - m_Textures: [] m_Width: 80 m_Height: 80 m_Kind: 3 m_SubKind: iPhone - m_Textures: [] m_Width: 80 m_Height: 80 m_Kind: 3 m_SubKind: iPad - m_Textures: [] m_Width: 40 m_Height: 40 m_Kind: 3 m_SubKind: iPad - m_Textures: [] m_Width: 87 m_Height: 87 m_Kind: 1 m_SubKind: iPhone - m_Textures: [] m_Width: 58 m_Height: 58 m_Kind: 1 m_SubKind: iPhone - m_Textures: [] m_Width: 29 m_Height: 29 m_Kind: 1 m_SubKind: iPhone - m_Textures: [] m_Width: 58 m_Height: 58 m_Kind: 1 m_SubKind: iPad - m_Textures: [] m_Width: 29 m_Height: 29 m_Kind: 1 m_SubKind: iPad - m_Textures: [] m_Width: 60 m_Height: 60 m_Kind: 2 m_SubKind: iPhone - m_Textures: [] m_Width: 40 m_Height: 40 m_Kind: 2 m_SubKind: iPhone - m_Textures: [] m_Width: 40 m_Height: 40 m_Kind: 2 m_SubKind: iPad - m_Textures: [] m_Width: 20 m_Height: 20 m_Kind: 2 m_SubKind: iPad - m_Textures: [] m_Width: 1024 m_Height: 1024 m_Kind: 4 m_SubKind: App Store - m_BuildTarget: Android m_Icons: - m_Textures: [] m_Width: 432 m_Height: 432 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 324 m_Height: 324 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 216 m_Height: 216 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 162 m_Height: 162 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 108 m_Height: 108 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 81 m_Height: 81 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 0 m_SubKind: m_BuildTargetBatching: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] m_BuildTargetGraphicsAPIs: - m_BuildTarget: iOSSupport m_APIs: 10000000 m_Automatic: 1 - m_BuildTarget: AndroidPlayer m_APIs: 0b00000008000000 m_Automatic: 0 m_BuildTargetVRSettings: [] openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 m_TemplateCustomTags: {} mobileMTRendering: Android: 1 iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetNormalMapEncoding: [] m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 cameraUsageDescription: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 switchSocketAllocatorPoolSize: 128 switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 switchUseGOLDLinker: 0 switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchTitleNames_0: switchTitleNames_1: switchTitleNames_2: switchTitleNames_3: switchTitleNames_4: switchTitleNames_5: switchTitleNames_6: switchTitleNames_7: switchTitleNames_8: switchTitleNames_9: switchTitleNames_10: switchTitleNames_11: switchTitleNames_12: switchTitleNames_13: switchTitleNames_14: switchTitleNames_15: switchPublisherNames_0: switchPublisherNames_1: switchPublisherNames_2: switchPublisherNames_3: switchPublisherNames_4: switchPublisherNames_5: switchPublisherNames_6: switchPublisherNames_7: switchPublisherNames_8: switchPublisherNames_9: switchPublisherNames_10: switchPublisherNames_11: switchPublisherNames_12: switchPublisherNames_13: switchPublisherNames_14: switchPublisherNames_15: switchIcons_0: {fileID: 0} switchIcons_1: {fileID: 0} switchIcons_2: {fileID: 0} switchIcons_3: {fileID: 0} switchIcons_4: {fileID: 0} switchIcons_5: {fileID: 0} switchIcons_6: {fileID: 0} switchIcons_7: {fileID: 0} switchIcons_8: {fileID: 0} switchIcons_9: {fileID: 0} switchIcons_10: {fileID: 0} switchIcons_11: {fileID: 0} switchIcons_12: {fileID: 0} switchIcons_13: {fileID: 0} switchIcons_14: {fileID: 0} switchIcons_15: {fileID: 0} switchSmallIcons_0: {fileID: 0} switchSmallIcons_1: {fileID: 0} switchSmallIcons_2: {fileID: 0} switchSmallIcons_3: {fileID: 0} switchSmallIcons_4: {fileID: 0} switchSmallIcons_5: {fileID: 0} switchSmallIcons_6: {fileID: 0} switchSmallIcons_7: {fileID: 0} switchSmallIcons_8: {fileID: 0} switchSmallIcons_9: {fileID: 0} switchSmallIcons_10: {fileID: 0} switchSmallIcons_11: {fileID: 0} switchSmallIcons_12: {fileID: 0} switchSmallIcons_13: {fileID: 0} switchSmallIcons_14: {fileID: 0} switchSmallIcons_15: {fileID: 0} switchManualHTML: switchAccessibleURLs: switchLegalInformation: switchMainThreadStackSize: 1048576 switchPresenceGroupId: switchLogoHandling: 0 switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 switchTouchScreenUsage: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 switchApplicationErrorCodeCategory: switchUserAccountSaveDataSize: 0 switchUserAccountSaveDataJournalSize: 0 switchApplicationAttribute: 0 switchCardSpecSize: -1 switchCardSpecClock: -1 switchRatingsMask: 0 switchRatingsInt_0: 0 switchRatingsInt_1: 0 switchRatingsInt_2: 0 switchRatingsInt_3: 0 switchRatingsInt_4: 0 switchRatingsInt_5: 0 switchRatingsInt_6: 0 switchRatingsInt_7: 0 switchRatingsInt_8: 0 switchRatingsInt_9: 0 switchRatingsInt_10: 0 switchRatingsInt_11: 0 switchRatingsInt_12: 0 switchLocalCommunicationIds_0: switchLocalCommunicationIds_1: switchLocalCommunicationIds_2: switchLocalCommunicationIds_3: switchLocalCommunicationIds_4: switchLocalCommunicationIds_5: switchLocalCommunicationIds_6: switchLocalCommunicationIds_7: switchParentalControl: 0 switchAllowsScreenshot: 1 switchAllowsVideoCapturing: 1 switchAllowsRuntimeAddOnContentInstall: 0 switchDataLossConfirmation: 0 switchUserAccountLockEnabled: 0 switchSystemResourceMemory: 16777216 switchSupportedNpadStyles: 22 switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 0 switchSupportedNpadCount: 8 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 switchTcpAutoSendBufferSizeMax: 256 switchTcpAutoReceiveBufferSizeMax: 256 switchUdpSendBufferSize: 9 switchUdpReceiveBufferSize: 42 switchSocketBufferEfficiency: 4 switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: ps4ParentalLevel: 11 ps4ContentID: ED1633-NPXX51362_00-0000000000000000 ps4Category: 0 ps4MasterVersion: 01.00 ps4AppVersion: 01.00 ps4AppType: 0 ps4ParamSfxPath: ps4VideoOutPixelFormat: 0 ps4VideoOutInitialWidth: 1920 ps4VideoOutBaseModeInitialWidth: 1920 ps4VideoOutReprojectionRate: 60 ps4PronunciationXMLPath: ps4PronunciationSIGPath: ps4BackgroundImagePath: ps4StartupImagePath: ps4StartupImagesFolder: ps4IconImagesFolder: ps4SaveDataImagePath: ps4SdkOverride: ps4BGMPath: ps4ShareFilePath: ps4ShareOverlayImagePath: ps4PrivacyGuardImagePath: ps4ExtraSceSysFile: ps4NPtitleDatPath: ps4RemotePlayKeyAssignment: -1 ps4RemotePlayKeyMappingDir: ps4PlayTogetherPlayerCount: 0 ps4EnterButtonAssignment: 2 ps4ApplicationParam1: 0 ps4ApplicationParam2: 0 ps4ApplicationParam3: 0 ps4ApplicationParam4: 0 ps4DownloadDataSize: 0 ps4GarlicHeapSize: 2048 ps4ProGarlicHeapSize: 2560 playerPrefsMaxSize: 32768 ps4Passcode: N2qmWqBlQ9wQj99nsQzldVI5ZuGXbEWR ps4pnSessions: 1 ps4pnPresence: 1 ps4pnFriends: 1 ps4pnGameCustomData: 1 playerPrefsSupport: 0 enableApplicationExit: 0 resetTempFolder: 1 restrictedAudioUsageRights: 0 ps4UseResolutionFallback: 0 ps4ReprojectionSupport: 0 ps4UseAudio3dBackend: 0 ps4UseLowGarlicFragmentationMode: 1 ps4SocialScreenEnabled: 0 ps4ScriptOptimizationLevel: 2 ps4Audio3dVirtualSpeakerCount: 14 ps4attribCpuUsage: 0 ps4PatchPkgPath: ps4PatchLatestPkgPath: ps4PatchChangeinfoPath: ps4PatchDayOne: 0 ps4attribUserManagement: 0 ps4attribMoveSupport: 0 ps4attrib3DSupport: 0 ps4attribShareSupport: 0 ps4attribExclusiveVR: 0 ps4disableAutoHideSplash: 0 ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 ps4CompatibilityPS5: 0 ps4AllowPS5Detection: 0 ps4GPU800MHz: 1 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} blurSplashScreenBackground: 1 spritePackerPolicy: webGLMemorySize: 32 webGLExceptionSupport: 1 webGLNameFilesAsHashes: 0 webGLDataCaching: 1 webGLDebugSymbols: 0 webGLEmscriptenArgs: webGLModulesDirectory: webGLTemplate: APPLICATION:Default webGLAnalyzeBuildSize: 0 webGLUseEmbeddedResources: 0 webGLCompressionFormat: 0 webGLWasmArithmeticExceptions: 0 webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLDecompressionFallback: 0 scriptingDefineSymbols: {} additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} suppressCommonWarnings: 1 allowUnsafeCode: 0 useDeterministicCompilation: 1 enableRoslynAnalyzers: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 0 assemblyVersionValidation: 1 gcWBarrierValidation: 0 apiCompatibilityLevelPerPlatform: {} m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: appsflyer-unity-plugin metroPackageVersion: metroCertificatePath: metroCertificatePassword: metroCertificateSubject: metroCertificateIssuer: metroCertificateNotAfter: 0000000000000000 metroApplicationDescription: appsflyer-unity-plugin wsaImages: {} metroTileShortName: metroTileShowName: 0 metroMediumTileShowName: 0 metroLargeTileShowName: 0 metroWideTileShowName: 0 metroSupportStreamingInstall: 0 metroLastRequiredScene: 0 metroDefaultTileSize: 1 metroTileForegroundText: 2 metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} metroFTAName: metroFTAFileTypes: [] metroProtocolName: vcxProjDefaultLanguage: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: XboxOneContentId: XboxOneTitleId: XboxOneSCId: XboxOneGameOsOverridePath: XboxOnePackagingOverridePath: XboxOneAppManifestOverridePath: XboxOneVersion: 1.0.0.0 XboxOnePackageEncryption: 0 XboxOnePackageUpdateGranularity: 2 XboxOneDescription: XboxOneLanguage: - enus XboxOneCapability: [] XboxOneGameRating: {} XboxOneIsContentPackage: 0 XboxOneEnhancedXboxCompatibilityMode: 0 XboxOneEnableGPUVariability: 1 XboxOneSockets: {} XboxOneSplashScreen: {fileID: 0} XboxOneAllowedProductIds: [] XboxOnePersistentLocalStorageSize: 0 XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: vrEditorSettings: {} cloudServicesEnabled: {} luminIcon: m_Name: m_ModelFolderPath: m_PortalFolderPath: luminCert: m_CertPath: m_SignPackage: 1 luminIsChannelApp: 0 luminVersion: m_VersionCode: 1 m_VersionName: apiCompatibilityLevel: 6 activeInputHandler: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] projectName: organizationId: cloudEnabled: 0 legacyClampBlendShapeWeights: 0 playerDataPath: forceSRGBBlit: 1 virtualTexturingSupportEnabled: 0 ================================================ FILE: ProjectSettings/ProjectVersion.txt ================================================ m_EditorVersion: 2020.3.41f1 m_EditorVersionWithRevision: 2020.3.41f1 (7c19dc9acfda) ================================================ FILE: ProjectSettings/QualitySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!47 &1 QualitySettings: m_ObjectHideFlags: 0 serializedVersion: 5 m_CurrentQuality: 5 m_QualitySettings: - serializedVersion: 2 name: Very Low pixelLightCount: 0 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 15 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 skinWeights: 1 textureQuality: 1 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 0 lodBias: 0.3 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 4 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Low pixelLightCount: 0 shadows: 0 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 20 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 skinWeights: 2 textureQuality: 0 anisotropicTextures: 0 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 0 lodBias: 0.4 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 16 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Medium pixelLightCount: 1 shadows: 1 shadowResolution: 0 shadowProjection: 1 shadowCascades: 1 shadowDistance: 20 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 0 skinWeights: 2 textureQuality: 0 anisotropicTextures: 1 antiAliasing: 0 softParticles: 0 softVegetation: 0 realtimeReflectionProbes: 0 billboardsFaceCameraPosition: 0 vSyncCount: 1 lodBias: 0.7 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 64 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: High pixelLightCount: 2 shadows: 2 shadowResolution: 1 shadowProjection: 1 shadowCascades: 2 shadowDistance: 40 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 skinWeights: 2 textureQuality: 0 anisotropicTextures: 1 antiAliasing: 0 softParticles: 0 softVegetation: 1 realtimeReflectionProbes: 1 billboardsFaceCameraPosition: 1 vSyncCount: 1 lodBias: 1 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 256 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Very High pixelLightCount: 3 shadows: 2 shadowResolution: 2 shadowProjection: 1 shadowCascades: 2 shadowDistance: 70 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 skinWeights: 4 textureQuality: 0 anisotropicTextures: 2 antiAliasing: 2 softParticles: 1 softVegetation: 1 realtimeReflectionProbes: 1 billboardsFaceCameraPosition: 1 vSyncCount: 1 lodBias: 1.5 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 1024 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] - serializedVersion: 2 name: Ultra pixelLightCount: 4 shadows: 2 shadowResolution: 2 shadowProjection: 1 shadowCascades: 4 shadowDistance: 150 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 skinWeights: 255 textureQuality: 0 anisotropicTextures: 2 antiAliasing: 2 softParticles: 1 softVegetation: 1 realtimeReflectionProbes: 1 billboardsFaceCameraPosition: 1 vSyncCount: 1 lodBias: 2 maximumLODLevel: 0 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 4096 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} excludedTargetPlatforms: [] m_PerPlatformDefaultQuality: Android: 2 CloudRendering: 5 GameCoreScarlett: 5 GameCoreXboxOne: 5 Lumin: 5 Nintendo Switch: 5 PS4: 5 PS5: 5 Stadia: 5 Standalone: 5 WebGL: 3 Windows Store Apps: 5 XboxOne: 5 iPhone: 2 tvOS: 2 ================================================ FILE: ProjectSettings/TagManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!78 &1 TagManager: serializedVersion: 2 tags: [] layers: - Default - TransparentFX - Ignore Raycast - - Water - UI - - - - - - - - - - - - - - - - - - - - - - - - - - m_SortingLayers: - name: Default uniqueID: 0 locked: 0 ================================================ FILE: ProjectSettings/TimeManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!5 &1 TimeManager: m_ObjectHideFlags: 0 Fixed Timestep: 0.02 Maximum Allowed Timestep: 0.33333334 m_TimeScale: 1 Maximum Particle Timestep: 0.03 ================================================ FILE: ProjectSettings/UnityConnectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!310 &1 UnityConnectSettings: m_ObjectHideFlags: 0 serializedVersion: 1 m_Enabled: 0 m_TestMode: 0 m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com m_TestInitMode: 0 CrashReportingSettings: m_EventUrl: https://perf-events.cloud.unity3d.com m_Enabled: 0 m_LogBufferSize: 10 m_CaptureEditorExceptions: 1 UnityPurchasingSettings: m_Enabled: 0 m_TestMode: 0 UnityAnalyticsSettings: m_Enabled: 0 m_TestMode: 0 m_InitializeOnStartup: 1 UnityAdsSettings: m_Enabled: 0 m_InitializeOnStartup: 1 m_TestMode: 0 m_IosGameId: m_AndroidGameId: m_GameIds: {} m_GameId: PerformanceReportingSettings: m_Enabled: 0 ================================================ FILE: ProjectSettings/VFXManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!937362698 &1 VFXManager: m_ObjectHideFlags: 0 m_IndirectShader: {fileID: 0} m_CopyBufferShader: {fileID: 0} m_SortShader: {fileID: 0} m_StripUpdateShader: {fileID: 0} m_RenderPipeSettingsPath: m_FixedTimeStep: 0.016666668 m_MaxDeltaTime: 0.05 ================================================ FILE: ProjectSettings/VersionControlSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!890905787 &1 VersionControlSettings: m_ObjectHideFlags: 0 m_Mode: Visible Meta Files m_CollabEditorSettings: inProgressEnabled: 1 ================================================ FILE: ProjectSettings/XRSettings.asset ================================================ { "m_SettingKeys": [ "VR Device Disabled", "VR Device User Alert" ], "m_SettingValues": [ "False", "False" ] } ================================================ FILE: README.md ================================================ # appsflyer-unity-plugin [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![GitHub tag](https://img.shields.io/github/v/release/AppsFlyerSDK/appsflyer-unity-plugin)](https://img.shields.io/github/v/release/AppsFlyerSDK/appsflyer-unity-plugin) [![Unit tests](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/actions/workflows/main.yml/badge.svg)](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/actions/workflows/main.yml) [![check packages](https://github.com/af-margot/appsflyer-unity-plugin-beta/actions/workflows/checksums_files.yml/badge.svg)](https://github.com/af-margot/appsflyer-unity-plugin-beta/actions/workflows/checksums_files.yml) 🛠 In order for us to provide optimal support, please contact AppsFlyer support through the Customer Assistant Chatbot for assistance with troubleshooting issues or product guidance.
To do so, please follow [this article](https://support.appsflyer.com/hc/en-us/articles/23583984402193-Using-the-Customer-Assistant-Chatbot) ## 📖 The Unity documentation also be found [here](https://dev.appsflyer.com/hc/docs/unity-plugin) ### This plugin is built for - Android AppsFlyer SDK v6.17.6 - Android Purchase Connector 2.2.0 - iOS AppsFlyer SDK v6.17.9 - iOS Purchase Connector 6.17.9 --- ## 📌 Important: Two Versions of Unity Plugin v6.17.9 We have released **two** versions of the AppsFlyer Unity plugin to support teams at different stages of migrating to **Google Play Billing Library v8.0.0**. ### Option A — `v6.17.91` (Billing Library v8) ← Latest - **What's included:** Support for **Google Play Billing Library 8.0.0** on Android (Android Purchase Connector version - 2.2.0). - **Impact:** This version may introduce **breaking changes** for apps that have **not yet migrated** to the Billing v8 APIs. - **Unity IAP requirement:** If you choose this option, update **Unity IAP (`com.unity.purchasing`) to version 5.0.0 or newer** (we recommend the latest 5.x). Unity IAP 4.x does **not** include Billing v8. ### Option B — `v6.17.90` (Billing Library v7) - **Purpose:** For developers **not ready** to adopt Billing v8. - **Bundled SDKs:** **iOS SDK 6.17.9** and **Android SDK 6.17.6**. - **Android Purchase Connector:** Version 2.2.0 - **Impact:** Lets you update the AppsFlyer SDKs without changing your existing (pre‑v8) billing integration. --- ## 🎉 New in 6.17.1 - Purchase Connector Integration - Starting from version 6.17.1, the **Purchase Connector is now integrated directly into the main AppsFlyer Unity plugin**. You no longer need to download, import, or maintain a separate Purchase Connector package. - If you were previously using the standalone Purchase Connector from a separate repository, simply remove any references to `using AppsFlyerConnector;` from your codebase, as its functionality is now included in the main plugin under the `AppsFlyerSDK` namespace. - The Purchase Connector now supports **StoreKit 2** for iOS 15+ alongside the existing StoreKit 1 support. - For detailed migration instructions and new features, see our [Purchase Connector documentation](/docs/purchase-connector.md). --- ## ❗❗ Breaking changes when updating to 6.17.5 ❗❗ - **In-App Purchase Validation API Changes**: The `validateAndSendInAppPurchase` method signatures have been updated for better type safety and cleaner code. - **V2 Methods (Recommended)**: New overloads using structured data classes (`AFPurchaseDetailsAndroid`/`AFSDKPurchaseDetailsIOS`) are now the recommended approach. - **Legacy Methods (Deprecated)**: The old string-based parameter methods are now deprecated but maintained for backward compatibility. - **Migration Required**: If you're using the old `validateAndSendInAppPurchase` methods, consider migrating to the V2 versions for better maintainability. - For detailed API documentation and migration examples, see our [API reference](/docs/API.md). --- ## ❗❗ Breaking changes when updating to 6.12.20 ❗❗ - Starting from version 6.12.20, we have changed the way we distribute the plugin via UPM. The UPM branches will no longer hold a dependency for `com.google.external-dependency-manager` as it was proved to cause issues in different versions of Unity - to be clear, this dependency is still required to utilize our plugin, we just can't distribute the plugin with it in UPM form as the EDM4U dependency is [not available via UPM for quite a while already](https://github.com/googlesamples/unity-jar-resolver/issues/434#issuecomment-827028132) but is still available via `.unitypackage` or `.tgz` files, if you use UPM to fetch our plugin - [please download a suitable version of EDM4U](https://github.com/googlesamples/unity-jar-resolver) so you will be able to resolve the dependencies, or opt for [an installation without EDM4U](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/docs/Installation.md#installation-without-unity-jar-resolver). --- ## ❗❗ Breaking changes when updating to 6.6.0 ❗❗ - Starting version 6.6.0, there is no more need to differentiate between iOS and Android APIs. All APIs must be called with `AppsFlyer` class (even if the API is only iOS or Android). - Please take into consideration that since version 6.6.0, most of the APIs require `initSDK` to be called prior to using them, and since version 6.10.10 only a handful of APIs will properly work when called prior to initialization: `setIsDebug`, `setCurrencyCode`, `setHost`, `disableSKAdNetwork`. Example: Before 6.6.0: ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyeriOS.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` --- After 6.6.0: ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` --- ## Strict Mode The plugin supports a Strict Mode which completely removes the IDFA collection functionality and AdSupport framework dependencies. Use the Strict Mode when developing apps for kids, for example. More information about how to install the Strict Mode is available [here](/docs/Installation.md). ### AD_ID permission for Android In v6.8.0 of the AppsFlyer SDK, we added the normal permission com.google.android.gms.permission.AD_ID to the SDK's AndroidManifest, to allow the SDK to collect the Android Advertising ID on apps targeting API 33. If your app is targeting children, you need to revoke this permission to comply with Google's Data policy. You can read more about it [here](https://dev.appsflyer.com/hc/docs/install-android-sdk#the-ad_id-permission). --- ## 🚀 Getting Started - [Installation](/docs/Installation.md) - [Integration](/docs/BasicIntegration.md) - [Test integration](/docs/Testing.md) - [In-app events](/docs/InAppEvents.md) - [Send Consent for DMA Compliance](/docs/DMAConsent.md) - [Uninstall measurement](/docs/UninstallMeasurement.md) ## 💰 Purchase Connector - [Purchase Connector (ROI360)](/docs/purchase-connector.md) ## 🔗 Deep Linking - [Integration](/docs/DeepLinkIntegrate.md) - [Unified Deep Link (UDL)](/docs/UnifiedDeepLink.md) - [User invite](/docs/UserInvite.md) ## 🧪 Sample App - [ButterFlyer](https://github.com/AppsFlyerSDK/appsflyer-unity-sample-app) ---- ### [API reference](/docs/API.md) ### [Troubleshooting](/docs/Troubleshooting.md) ================================================ FILE: UserSettings/EditorUserSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!162 &1 EditorUserSettings: m_ObjectHideFlags: 0 serializedVersion: 4 m_ConfigSettings: vcSharedLogLevel: value: 0d5e400f0650 flags: 0 m_VCAutomaticAdd: 1 m_VCDebugCom: 0 m_VCDebugCmd: 0 m_VCDebugOut: 0 m_SemanticMergeMode: 2 m_DesiredImportWorkerCount: 2 m_StandbyImportWorkerCount: 2 m_IdleImportWorkerShutdownDelay: 60000 m_VCShowFailedCheckout: 1 m_VCOverwriteFailedCheckoutAssets: 1 m_VCProjectOverlayIcons: 1 m_VCHierarchyOverlayIcons: 1 m_VCOtherOverlayIcons: 1 m_VCAllowAsyncUpdate: 1 m_ArtifactGarbageCollection: 1 ================================================ FILE: UserSettings/Layouts/default-2021.dwlt ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12004, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_PixelRect: serializedVersion: 2 x: 0 y: 53 width: 1440 height: 847 m_ShowMode: 4 m_Title: Project m_RootView: {fileID: 6} m_MinSize: {x: 875, y: 300} m_MaxSize: {x: 10000, y: 10000} m_Maximized: 0 --- !u!114 &2 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 9} - {fileID: 3} m_Position: serializedVersion: 2 x: 0 y: 30 width: 1440 height: 797 m_MinSize: {x: 300, y: 200} m_MaxSize: {x: 24288, y: 16192} vertical: 0 controlID: 16 --- !u!114 &3 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 1148 y: 0 width: 292 height: 797 m_MinSize: {x: 276, y: 71} m_MaxSize: {x: 4001, y: 4021} m_ActualView: {fileID: 14} m_Panes: - {fileID: 14} m_Selected: 0 m_LastSelected: 0 --- !u!114 &4 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 0 width: 284 height: 482 m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_ActualView: {fileID: 15} m_Panes: - {fileID: 15} m_Selected: 0 m_LastSelected: 0 --- !u!114 &5 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: ProjectBrowser m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 482 width: 1148 height: 315 m_MinSize: {x: 231, y: 271} m_MaxSize: {x: 10001, y: 10021} m_ActualView: {fileID: 13} m_Panes: - {fileID: 13} - {fileID: 18} m_Selected: 0 m_LastSelected: 1 --- !u!114 &6 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12008, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 7} - {fileID: 2} - {fileID: 8} m_Position: serializedVersion: 2 x: 0 y: 0 width: 1440 height: 847 m_MinSize: {x: 875, y: 300} m_MaxSize: {x: 10000, y: 10000} m_UseTopView: 1 m_TopViewHeight: 30 m_UseBottomView: 1 m_BottomViewHeight: 20 --- !u!114 &7 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12011, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 0 width: 1440 height: 30 m_MinSize: {x: 0, y: 0} m_MaxSize: {x: 0, y: 0} m_LastLoadedLayoutName: --- !u!114 &8 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12042, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 827 width: 1440 height: 20 m_MinSize: {x: 0, y: 0} m_MaxSize: {x: 0, y: 0} --- !u!114 &9 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 10} - {fileID: 5} m_Position: serializedVersion: 2 x: 0 y: 0 width: 1148 height: 797 m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 16192, y: 16192} vertical: 1 controlID: 17 --- !u!114 &10 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 4} - {fileID: 11} m_Position: serializedVersion: 2 x: 0 y: 0 width: 1148 height: 482 m_MinSize: {x: 200, y: 100} m_MaxSize: {x: 16192, y: 8096} vertical: 0 controlID: 18 --- !u!114 &11 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 284 y: 0 width: 864 height: 482 m_MinSize: {x: 202, y: 221} m_MaxSize: {x: 4002, y: 4021} m_ActualView: {fileID: 16} m_Panes: - {fileID: 16} - {fileID: 17} - {fileID: 12} m_Selected: 0 m_LastSelected: 1 --- !u!114 &12 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12111, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 400, y: 100} m_MaxSize: {x: 2048, y: 2048} m_TitleContent: m_Text: Asset Store m_Image: {fileID: -7444545952099596278, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 468 y: 181 width: 973 height: 501 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] --- !u!114 &13 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12014, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 230, y: 250} m_MaxSize: {x: 10000, y: 10000} m_TitleContent: m_Text: Project m_Image: {fileID: -5179483145760003458, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 0 y: 565 width: 1147 height: 294 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_SearchFilter: m_NameFilter: m_ClassNames: [] m_AssetLabels: [] m_AssetBundleNames: [] m_VersionControlStates: [] m_SoftLockControlStates: [] m_ReferencingInstanceIDs: m_SceneHandles: m_ShowAllHits: 0 m_SkipHidden: 0 m_SearchArea: 1 m_Folders: - Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle m_Globs: [] m_OriginalText: m_ViewMode: 1 m_StartGridSize: 64 m_LastFolders: - Assets/AppsFlyer/Mac/AppsFlyerBundle.bundle m_LastFoldersGridSize: -1 m_LastProjectPath: /Users/margotguetta/Desktop/Projects/Plugin/Unity/appsflyer-unity-plugin m_LockTracker: m_IsLocked: 0 m_FolderTreeState: scrollPos: {x: 0, y: 0} m_SelectedIDs: 4e060000 m_LastClickedID: 1614 m_ExpandedIDs: 00000000ee5e0000f05e0000f25e00000e5f000000ca9a3bffffff7f m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: Mac m_OriginalName: Mac m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 24334 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 0 m_IsRenamingFilename: 1 m_ClientGUIView: {fileID: 5} m_SearchString: m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_AssetTreeState: scrollPos: {x: 0, y: 0} m_SelectedIDs: m_LastClickedID: 0 m_ExpandedIDs: 00000000ee5e0000f05e0000f25e0000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 1 m_ClientGUIView: {fileID: 0} m_SearchString: m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_ListAreaState: m_SelectedInstanceIDs: m_LastClickedInstanceID: 0 m_HadKeyboardFocusLastEvent: 0 m_ExpandedInstanceIDs: c6230000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 1 m_ClientGUIView: {fileID: 0} m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_NewAssetIndexInList: -1 m_ScrollPosition: {x: 0, y: 0} m_GridSize: 64 m_SkipHiddenPackages: 0 m_DirectoriesAreaWidth: 115 --- !u!114 &14 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12019, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 275, y: 50} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Inspector m_Image: {fileID: -440750813802333266, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 1148 y: 83 width: 291 height: 776 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ObjectsLockedBeforeSerialization: [] m_InstanceIDsLockedBeforeSerialization: m_PreviewResizer: m_CachedPref: 160 m_ControlHash: -371814159 m_PrefName: Preview_InspectorPreview m_LastInspectedObjectInstanceID: -1 m_LastVerticalScrollValue: 0 m_GlobalObjectId: m_InspectorMode: 0 m_LockTracker: m_IsLocked: 0 m_PreviewWindow: {fileID: 0} --- !u!114 &15 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12061, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Hierarchy m_Image: {fileID: -3734745235275155857, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 0 y: 83 width: 283 height: 461 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_SceneHierarchy: m_TreeViewState: scrollPos: {x: 0, y: 0} m_SelectedIDs: 4e060000 m_LastClickedID: 0 m_ExpandedIDs: c6faffff m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 0 m_ClientGUIView: {fileID: 0} m_SearchString: m_ExpandedScenes: [] m_CurrenRootInstanceID: 0 m_LockTracker: m_IsLocked: 0 m_CurrentSortingName: TransformSorting m_WindowGUID: 9a5ee93bb0e91428facd2b3497f49068 --- !u!114 &16 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12013, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Scene m_Image: {fileID: 8634526014445323508, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 284 y: 83 width: 862 height: 461 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: - dockPosition: 0 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 1 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: -98, y: -26} snapCorner: 3 id: Tool Settings index: 0 layout: 1 - dockPosition: 0 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 1 snapOffset: {x: -141, y: 149} snapOffsetDelta: {x: 0, y: 0} snapCorner: 1 id: unity-grid-and-snap-toolbar index: 1 layout: 1 - dockPosition: 1 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 1 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: unity-scene-view-toolbar index: 0 layout: 1 - dockPosition: 1 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 1 id: unity-search-toolbar index: 1 layout: 1 - dockPosition: 1 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Open Tile Palette index: 2 layout: 4 - dockPosition: 1 containerId: overlay-toolbar__top floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Tilemap Focus index: 3 layout: 4 - dockPosition: 0 containerId: overlay-container--left floating: 0 collapsed: 0 displayed: 1 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: unity-transform-toolbar index: 0 layout: 2 - dockPosition: 0 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 1 snapOffset: {x: 67.5, y: 86} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Orientation index: 0 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Light Settings index: 0 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Camera index: 1 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Cloth Constraints index: 2 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Cloth Collisions index: 3 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Navmesh Display index: 4 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Agent Display index: 5 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Obstacle Display index: 6 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Occlusion Culling index: 7 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Physics Debugger index: 8 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Scene Visibility index: 9 layout: 4 - dockPosition: 1 containerId: overlay-container--right floating: 0 collapsed: 0 displayed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 id: Scene View/Particles index: 10 layout: 4 m_WindowGUID: 1acd90b02262845b2a933768b9dd2124 m_Gizmos: 1 m_OverrideSceneCullingMask: 6917529027641081856 m_SceneIsLit: 1 m_SceneLighting: 1 m_2DMode: 0 m_isRotationLocked: 0 m_PlayAudio: 0 m_AudioPlay: 0 m_Position: m_Target: {x: 0, y: 0, z: 0} speed: 2 m_Value: {x: 0, y: 0, z: 0} m_RenderMode: 0 m_CameraMode: drawMode: 0 name: Shaded section: Shading Mode m_ValidateTrueMetals: 0 m_DoValidateTrueMetals: 0 m_ExposureSliderValue: 0 m_SceneViewState: m_AlwaysRefresh: 0 showFog: 1 showSkybox: 1 showFlares: 1 showImageEffects: 1 showParticleSystems: 1 showVisualEffectGraphs: 1 m_FxEnabled: 1 m_Grid: xGrid: m_Fade: m_Target: 0 speed: 2 m_Value: 0 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 0, y: 0} yGrid: m_Fade: m_Target: 1 speed: 2 m_Value: 1 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 1, y: 1} zGrid: m_Fade: m_Target: 0 speed: 2 m_Value: 0 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 0, y: 0} m_ShowGrid: 1 m_GridAxis: 1 m_gridOpacity: 0.5 m_Rotation: m_Target: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226} speed: 2 m_Value: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226} m_Size: m_Target: 10 speed: 2 m_Value: 10 m_Ortho: m_Target: 0 speed: 2 m_Value: 0 m_CameraSettings: m_Speed: 1 m_SpeedNormalized: 0.5 m_SpeedMin: 0.01 m_SpeedMax: 2 m_EasingEnabled: 1 m_EasingDuration: 0.4 m_AccelerationEnabled: 1 m_FieldOfViewHorizontalOrVertical: 60 m_NearClip: 0.03 m_FarClip: 10000 m_DynamicClip: 1 m_OcclusionCulling: 0 m_LastSceneViewRotation: {x: 0, y: 0, z: 0, w: 0} m_LastSceneViewOrtho: 0 m_ReplacementShader: {fileID: 0} m_ReplacementString: m_SceneVisActive: 1 m_LastLockedObject: {fileID: 0} m_ViewIsLockedToObject: 0 --- !u!114 &17 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12015, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Game m_Image: {fileID: 4621777727084837110, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 507 y: 94 width: 1532 height: 790 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_SerializedViewNames: [] m_SerializedViewValues: [] m_PlayModeViewName: GameView m_ShowGizmos: 0 m_TargetDisplay: 0 m_ClearColor: {r: 0, g: 0, b: 0, a: 0} m_TargetSize: {x: 3064, y: 1538} m_TextureFilterMode: 0 m_TextureHideFlags: 61 m_RenderIMGUI: 0 m_EnterPlayModeBehavior: 0 m_UseMipMap: 0 m_VSyncEnabled: 0 m_Gizmos: 0 m_Stats: 0 m_SelectedSizes: 00000000000000000000000000000000000000000000000000000000000000000000000000000000 m_ZoomArea: m_HRangeLocked: 0 m_VRangeLocked: 0 hZoomLockedByDefault: 0 vZoomLockedByDefault: 0 m_HBaseRangeMin: -766 m_HBaseRangeMax: 766 m_VBaseRangeMin: -384.5 m_VBaseRangeMax: 384.5 m_HAllowExceedBaseRangeMin: 1 m_HAllowExceedBaseRangeMax: 1 m_VAllowExceedBaseRangeMin: 1 m_VAllowExceedBaseRangeMax: 1 m_ScaleWithWindow: 0 m_HSlider: 0 m_VSlider: 0 m_IgnoreScrollWheelUntilClicked: 0 m_EnableMouseInput: 1 m_EnableSliderZoomHorizontal: 0 m_EnableSliderZoomVertical: 0 m_UniformScale: 1 m_UpDirection: 1 m_DrawArea: serializedVersion: 2 x: 0 y: 21 width: 1532 height: 769 m_Scale: {x: 1, y: 1} m_Translation: {x: 766, y: 384.5} m_MarginLeft: 0 m_MarginRight: 0 m_MarginTop: 0 m_MarginBottom: 0 m_LastShownAreaInsideMargins: serializedVersion: 2 x: -766 y: -384.5 width: 1532 height: 769 m_MinimalGUI: 1 m_defaultScale: 1 m_LastWindowPixelSize: {x: 3064, y: 1580} m_ClearInEditMode: 1 m_NoCameraWarning: 1 m_LowResolutionForAspectRatios: 00000000000000000000 m_XRRenderMode: 0 m_RenderTexture: {fileID: 0} --- !u!114 &18 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12003, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 100, y: 100} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Console m_Image: {fileID: -4950941429401207979, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_Pos: serializedVersion: 2 x: 0 y: 565 width: 1147 height: 294 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] ================================================ FILE: UserSettings/Layouts/default-6000.dwlt ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 12004, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_PixelRect: serializedVersion: 2 x: 2250 y: 247 width: 1256 height: 724 m_ShowMode: 0 m_Title: Package Manager m_RootView: {fileID: 4} m_MinSize: {x: 748, y: 276} m_MaxSize: {x: 4000, y: 4026} m_Maximized: 0 --- !u!114 &2 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12004, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_PixelRect: serializedVersion: 2 x: 1792 y: -153 width: 2560 height: 1333 m_ShowMode: 4 m_Title: Project m_RootView: {fileID: 9} m_MinSize: {x: 875, y: 300} m_MaxSize: {x: 10000, y: 10000} m_Maximized: 1 --- !u!114 &3 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: PackageManagerWindow m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 0 width: 1256 height: 724 m_MinSize: {x: 748, y: 276} m_MaxSize: {x: 4000, y: 4026} m_ActualView: {fileID: 15} m_Panes: - {fileID: 15} m_Selected: 0 m_LastSelected: 0 --- !u!114 &4 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 3} m_Position: serializedVersion: 2 x: 0 y: 0 width: 1256 height: 724 m_MinSize: {x: 748, y: 276} m_MaxSize: {x: 4000, y: 4026} vertical: 0 controlID: 14 draggingID: 0 --- !u!114 &5 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 12} - {fileID: 6} m_Position: serializedVersion: 2 x: 0 y: 36 width: 2560 height: 1277 m_MinSize: {x: 300, y: 100} m_MaxSize: {x: 24288, y: 16192} vertical: 0 controlID: 50 draggingID: 0 --- !u!114 &6 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 2039 y: 0 width: 521 height: 1277 m_MinSize: {x: 275, y: 50} m_MaxSize: {x: 4000, y: 4000} m_ActualView: {fileID: 18} m_Panes: - {fileID: 18} m_Selected: 0 m_LastSelected: 0 --- !u!114 &7 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 0 width: 504 height: 772 m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_ActualView: {fileID: 19} m_Panes: - {fileID: 19} m_Selected: 0 m_LastSelected: 0 --- !u!114 &8 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: ProjectBrowser m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 772 width: 2039 height: 505 m_MinSize: {x: 231, y: 276} m_MaxSize: {x: 10001, y: 10026} m_ActualView: {fileID: 17} m_Panes: - {fileID: 17} - {fileID: 22} m_Selected: 0 m_LastSelected: 1 --- !u!114 &9 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12008, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 10} - {fileID: 5} - {fileID: 11} m_Position: serializedVersion: 2 x: 0 y: 0 width: 2560 height: 1333 m_MinSize: {x: 875, y: 300} m_MaxSize: {x: 10000, y: 10000} m_UseTopView: 1 m_TopViewHeight: 36 m_UseBottomView: 1 m_BottomViewHeight: 20 --- !u!114 &10 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12011, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 0 width: 2560 height: 36 m_MinSize: {x: 0, y: 0} m_MaxSize: {x: 0, y: 0} m_LastLoadedLayoutName: --- !u!114 &11 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12042, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 0 y: 1313 width: 2560 height: 20 m_MinSize: {x: 0, y: 0} m_MaxSize: {x: 0, y: 0} --- !u!114 &12 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 13} - {fileID: 8} m_Position: serializedVersion: 2 x: 0 y: 0 width: 2039 height: 1277 m_MinSize: {x: 200, y: 100} m_MaxSize: {x: 16192, y: 16192} vertical: 1 controlID: 51 draggingID: 0 --- !u!114 &13 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: - {fileID: 7} - {fileID: 14} m_Position: serializedVersion: 2 x: 0 y: 0 width: 2039 height: 772 m_MinSize: {x: 200, y: 50} m_MaxSize: {x: 16192, y: 8096} vertical: 0 controlID: 42 draggingID: 0 --- !u!114 &14 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Children: [] m_Position: serializedVersion: 2 x: 504 y: 0 width: 1535 height: 772 m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_ActualView: {fileID: 20} m_Panes: - {fileID: 20} - {fileID: 21} - {fileID: 16} m_Selected: 0 m_LastSelected: 1 --- !u!114 &15 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 13953, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 748, y: 250} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Package Manager m_Image: {fileID: -2824328813065806953, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Package Manager\u200B" m_Pos: serializedVersion: 2 x: 2250 y: 247 width: 1256 height: 698 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 --- !u!114 &16 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12111, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 400, y: 100} m_MaxSize: {x: 2048, y: 2048} m_TitleContent: m_Text: Asset Store m_Image: {fileID: -8693916549880196297, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Asset Store\u200B" m_Pos: serializedVersion: 2 x: 468 y: 181 width: 973 height: 501 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 --- !u!114 &17 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12014, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 230, y: 250} m_MaxSize: {x: 10000, y: 10000} m_TitleContent: m_Text: Project m_Image: {fileID: -5467254957812901981, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Project\u200B" m_Pos: serializedVersion: 2 x: 1792 y: 655 width: 2038 height: 479 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 m_SearchFilter: m_NameFilter: m_ClassNames: [] m_AssetLabels: [] m_AssetBundleNames: [] m_ReferencingInstanceIDs: m_SceneHandles: m_ShowAllHits: 0 m_SkipHidden: 0 m_SearchArea: 1 m_Folders: - Packages m_Globs: [] m_ProductIds: m_AnyWithAssetOrigin: 0 m_OriginalText: m_ImportLogFlags: 0 m_FilterByTypeIntersection: 0 m_ViewMode: 1 m_StartGridSize: 64 m_LastFolders: - Packages m_LastFoldersGridSize: -1 m_LastProjectPath: /Users/veronicabelyakov/Applications/appsflyer-unity-plugin m_LockTracker: m_IsLocked: 0 m_FolderTreeState: scrollPos: {x: 0, y: 0} m_SelectedIDs: ffffff7f m_LastClickedID: 2147483647 m_ExpandedIDs: 000000007049000000ca9a3bffffff7f m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 1 m_TrimLeadingAndTrailingWhitespace: 0 m_ClientGUIView: {fileID: 8} m_SearchString: m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_AssetTreeState: scrollPos: {x: 0, y: 0} m_SelectedIDs: m_LastClickedID: 0 m_ExpandedIDs: m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 1 m_TrimLeadingAndTrailingWhitespace: 0 m_ClientGUIView: {fileID: 0} m_SearchString: m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_ListAreaState: m_SelectedInstanceIDs: m_LastClickedInstanceID: 0 m_HadKeyboardFocusLastEvent: 0 m_ExpandedInstanceIDs: c6230000 m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 1 m_TrimLeadingAndTrailingWhitespace: 0 m_ClientGUIView: {fileID: 0} m_CreateAssetUtility: m_EndAction: {fileID: 0} m_InstanceID: 0 m_Path: m_Icon: {fileID: 0} m_ResourceFile: m_NewAssetIndexInList: -1 m_ScrollPosition: {x: 0, y: 0} m_GridSize: 64 m_SkipHiddenPackages: 0 m_DirectoriesAreaWidth: 246 --- !u!114 &18 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12019, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 275, y: 50} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Inspector m_Image: {fileID: -2667387946076563598, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Inspector\u200B" m_Pos: serializedVersion: 2 x: 3831 y: -117 width: 520 height: 1251 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 m_ObjectsLockedBeforeSerialization: [] m_InstanceIDsLockedBeforeSerialization: m_PreviewResizer: m_CachedPref: 160 m_ControlHash: -371814159 m_PrefName: Preview_InspectorPreview m_LastInspectedObjectInstanceID: -1 m_LastVerticalScrollValue: 0 m_GlobalObjectId: m_InspectorMode: 0 m_LockTracker: m_IsLocked: 0 m_PreviewWindow: {fileID: 0} --- !u!114 &19 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12061, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Hierarchy m_Image: {fileID: 7966133145522015247, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Hierarchy\u200B" m_Pos: serializedVersion: 2 x: 1792 y: -117 width: 503 height: 746 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 m_SceneHierarchy: m_TreeViewState: scrollPos: {x: 0, y: 0} m_SelectedIDs: m_LastClickedID: 0 m_ExpandedIDs: b6f1ffff m_RenameOverlay: m_UserAcceptedRename: 0 m_Name: m_OriginalName: m_EditFieldRect: serializedVersion: 2 x: 0 y: 0 width: 0 height: 0 m_UserData: 0 m_IsWaitingForDelay: 0 m_IsRenaming: 0 m_OriginalEventType: 11 m_IsRenamingFilename: 0 m_TrimLeadingAndTrailingWhitespace: 0 m_ClientGUIView: {fileID: 0} m_SearchString: m_ExpandedScenes: [] m_CurrenRootInstanceID: 0 m_LockTracker: m_IsLocked: 0 m_CurrentSortingName: TransformSorting m_WindowGUID: 9a5ee93bb0e91428facd2b3497f49068 --- !u!114 &20 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12013, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Scene m_Image: {fileID: 2593428753322112591, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Scene\u200B" m_Pos: serializedVersion: 2 x: 2296 y: -117 width: 1533 height: 746 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: - dockPosition: 0 containerId: overlay-toolbar__top displayed: 1 id: Tool Settings index: 0 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-24.0,"y":-24.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: -24, y: -24} snapOffsetDelta: {x: 0, y: 0} snapCorner: 3 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-toolbar__top displayed: 1 id: unity-grid-and-snap-toolbar index: 1 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-141.0,"y":149.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":1,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: -141, y: 149} snapOffsetDelta: {x: 0, y: 0} snapCorner: 1 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-toolbar__top displayed: 1 id: unity-scene-view-toolbar index: 0 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 24, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-toolbar__top displayed: 0 id: unity-search-toolbar index: 1 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":-24.0,"y":0.0},"m_FloatingSnapCorner":1,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: -24, y: 0} snapCorner: 1 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-toolbar__top displayed: 0 id: Scene View/Open Tile Palette index: 2 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-toolbar__top displayed: 0 id: Scene View/Tilemap Focus index: 3 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-container--left displayed: 1 id: unity-transform-toolbar index: 0 contents: '{"m_Layout":2,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 24, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 2 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-container--right displayed: 1 id: Orientation index: 0 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":67.5,"y":86.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 67.5, y: 86} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Light Settings index: 0 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Camera index: 1 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Cloth Constraints index: 1 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Cloth Collisions index: 2 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Navmesh Display index: 4 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Agent Display index: 5 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Obstacle Display index: 6 contents: floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Occlusion Culling index: 3 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Physics Debugger index: 4 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Scene Visibility index: 5 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/Particles index: 6 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-toolbar__top displayed: 0 id: Brush Attributes index: 2 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-toolbar__top displayed: 1 id: unity-scene-view-camera-mode-toolbar index: 2 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 24, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-toolbar__left displayed: 0 id: Terrain Tools index: 0 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 0 containerId: overlay-toolbar__left displayed: 0 id: Brush Masks index: 1 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--left displayed: 0 id: Scene View/Lighting Visualization Colors index: 0 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--left displayed: 1 id: Overlays/OverlayMenu index: 1 contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 24, y: 0} snapOffsetDelta: {x: 0, y: 0} snapCorner: 0 layout: 1 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: SceneView/CamerasOverlay index: 7 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/PBR Validation Settings index: 8 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 - dockPosition: 1 containerId: overlay-container--right displayed: 0 id: Scene View/TrailRenderer index: 9 contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' floating: 0 collapsed: 0 snapOffset: {x: 0, y: 0} snapOffsetDelta: {x: 24, y: 0} snapCorner: 0 layout: 4 size: {x: 0, y: 0} sizeOverridden: 0 m_ContainerData: - containerId: overlay-toolbar__top scrollOffset: 0 - containerId: overlay-toolbar__left scrollOffset: 0 - containerId: overlay-container--left scrollOffset: 0 - containerId: overlay-container--right scrollOffset: 0 - containerId: overlay-toolbar__right scrollOffset: 0 - containerId: overlay-toolbar__bottom scrollOffset: 0 - containerId: Floating scrollOffset: 0 m_OverlaysVisible: 1 m_WindowGUID: 1acd90b02262845b2a933768b9dd2124 m_Gizmos: 1 m_OverrideSceneCullingMask: 6917529027641081856 m_SceneIsLit: 1 m_SceneLighting: 1 m_2DMode: 0 m_isRotationLocked: 0 m_PlayAudio: 0 m_AudioPlay: 0 m_DebugDrawModesUseInteractiveLightBakingData: 0 m_Position: m_Target: {x: 0, y: 0, z: 0} speed: 2 m_Value: {x: 0, y: 0, z: 0} m_RenderMode: 0 m_CameraMode: drawMode: 0 name: Shaded section: Shading Mode m_ValidateTrueMetals: 0 m_DoValidateTrueMetals: 0 m_SceneViewState: m_AlwaysRefresh: 0 showFog: 1 showSkybox: 1 showFlares: 1 showImageEffects: 1 showParticleSystems: 1 showVisualEffectGraphs: 1 m_FxEnabled: 1 m_Grid: xGrid: m_Fade: m_Target: 0 speed: 2 m_Value: 0 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 0, y: 0} yGrid: m_Fade: m_Target: 1 speed: 2 m_Value: 1 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 1, y: 1} zGrid: m_Fade: m_Target: 0 speed: 2 m_Value: 0 m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} m_Pivot: {x: 0, y: 0, z: 0} m_Size: {x: 0, y: 0} m_ShowGrid: 1 m_GridAxis: 1 m_gridOpacity: 0.5 m_Rotation: m_Target: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226} speed: 2 m_Value: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226} m_Size: m_Target: 10 speed: 2 m_Value: 10 m_Ortho: m_Target: 0 speed: 2 m_Value: 0 m_CameraSettings: m_Speed: 1 m_SpeedNormalized: 0.5 m_SpeedMin: 0.01 m_SpeedMax: 2 m_EasingEnabled: 1 m_EasingDuration: 0.4 m_AccelerationEnabled: 1 m_FieldOfViewHorizontalOrVertical: 60 m_NearClip: 0.03 m_FarClip: 10000 m_DynamicClip: 1 m_OcclusionCulling: 0 m_LastSceneViewRotation: {x: 0, y: 0, z: 0, w: 0} m_LastSceneViewOrtho: 0 m_Viewpoint: m_SceneView: {fileID: 20} m_CameraOverscanSettings: m_Opacity: 50 m_Scale: 1 m_ReplacementShader: {fileID: 0} m_ReplacementString: m_SceneVisActive: 1 m_LastLockedObject: {fileID: 0} m_LastDebugDrawMode: 35 m_ViewIsLockedToObject: 0 --- !u!114 &21 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12015, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 200, y: 200} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Game m_Image: {fileID: -6423792434712278376, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Game\u200B" m_Pos: serializedVersion: 2 x: 507 y: 94 width: 1532 height: 790 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 m_SerializedViewNames: [] m_SerializedViewValues: [] m_PlayModeViewName: GameView m_ShowGizmos: 0 m_TargetDisplay: 0 m_ClearColor: {r: 0, g: 0, b: 0, a: 0} m_TargetSize: {x: 1532, y: 769} m_TextureFilterMode: 0 m_TextureHideFlags: 61 m_RenderIMGUI: 0 m_EnterPlayModeBehavior: 0 m_UseMipMap: 0 m_VSyncEnabled: 0 m_Gizmos: 0 m_Stats: 0 m_SelectedSizes: 00000000000000000000000000000000000000000000000000000000000000000000000000000000 m_ZoomArea: m_HRangeLocked: 0 m_VRangeLocked: 0 hZoomLockedByDefault: 0 vZoomLockedByDefault: 0 m_HBaseRangeMin: -766 m_HBaseRangeMax: 766 m_VBaseRangeMin: -384.5 m_VBaseRangeMax: 384.5 m_HAllowExceedBaseRangeMin: 1 m_HAllowExceedBaseRangeMax: 1 m_VAllowExceedBaseRangeMin: 1 m_VAllowExceedBaseRangeMax: 1 m_ScaleWithWindow: 0 m_HSlider: 0 m_VSlider: 0 m_IgnoreScrollWheelUntilClicked: 0 m_EnableMouseInput: 1 m_EnableSliderZoomHorizontal: 0 m_EnableSliderZoomVertical: 0 m_UniformScale: 1 m_UpDirection: 1 m_DrawArea: serializedVersion: 2 x: 0 y: 21 width: 1532 height: 769 m_Scale: {x: 1, y: 1} m_Translation: {x: 766, y: 384.5} m_MarginLeft: 0 m_MarginRight: 0 m_MarginTop: 0 m_MarginBottom: 0 m_LastShownAreaInsideMargins: serializedVersion: 2 x: -766 y: -384.5 width: 1532 height: 769 m_MinimalGUI: 1 m_defaultScale: 1 m_LastWindowPixelSize: {x: 1532, y: 790} m_ClearInEditMode: 1 m_NoCameraWarning: 1 m_LowResolutionForAspectRatios: 00000000000000000000 m_XRRenderMode: 0 m_RenderTexture: {fileID: 0} m_showToolbar: 1 --- !u!114 &22 MonoBehaviour: m_ObjectHideFlags: 52 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 1 m_Script: {fileID: 12003, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_MinSize: {x: 100, y: 100} m_MaxSize: {x: 4000, y: 4000} m_TitleContent: m_Text: Console m_Image: {fileID: -4327648978806127646, guid: 0000000000000000d000000000000000, type: 0} m_Tooltip: m_TextWithWhitespace: "Console\u200B" m_Pos: serializedVersion: 2 x: 1792 y: 655 width: 2038 height: 479 m_SerializedDataModeController: m_DataMode: 0 m_PreferredDataMode: 0 m_SupportedDataModes: isAutomatic: 1 m_ViewDataDictionary: {fileID: 0} m_OverlayCanvas: m_LastAppliedPresetName: Default m_SaveData: [] m_ContainerData: [] m_OverlaysVisible: 1 ================================================ FILE: UserSettings/Search.settings ================================================ {} ================================================ FILE: android-unity-wrapper/.gitignore ================================================ *.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx # Signing keys signing-key.gpg temp_key.asc *.asc *.gpg # Gradle properties with secrets gradle.properties ================================================ FILE: android-unity-wrapper/app/.gitignore ================================================ /build ================================================ FILE: android-unity-wrapper/app/build.gradle ================================================ apply plugin: 'com.android.application' android { namespace 'com.appsflyer.unitywrapper' compileSdkVersion 34 defaultConfig { applicationId "com.appsflyer.unitywrapper" minSdkVersion 16 targetSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } ================================================ FILE: android-unity-wrapper/app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: android-unity-wrapper/app/src/androidTest/java/com/appsflyer/unity/ExampleInstrumentedTest.java ================================================ package com.appsflyer.unity; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.appsflyer.unitywrapper", appContext.getPackageName()); } } ================================================ FILE: android-unity-wrapper/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/main/res/values/colors.xml ================================================ #6200EE #3700B3 #03DAC5 ================================================ FILE: android-unity-wrapper/app/src/main/res/values/strings.xml ================================================ android-unity-wrapper ================================================ FILE: android-unity-wrapper/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: android-unity-wrapper/app/src/test/java/com/appsflyer/unity/ExampleUnitTest.java ================================================ package com.appsflyer.unity; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() { assertEquals(4, 2 + 2); } } ================================================ FILE: android-unity-wrapper/build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { agp_version = '8.5.0' agp_version1 = '7.4.2' agp_version2 = '7.1.0' agp_version3 = '3.6.1' agp_version4 = '8.10.1' } repositories { google() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:$agp_version4" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } plugins { id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' } def getSonatypeRepositoryToken() { return hasProperty('ossrhToken') ? ossrhToken : "" } def getSonatypeRepositoryTokenPassword() { return hasProperty('ossrhTokenPassword') ? ossrhTokenPassword : "" } nexusPublishing { packageGroup = GROUP // optional if packageGroup == project.getGroup() repositories { sonatype { nexusUrl = uri("https://ossrh-staging-api.central.sonatype.com/service/local/") snapshotRepositoryUrl = uri("https://central.sonatype.com/repository/maven-snapshots/") username = getSonatypeRepositoryToken() password = getSonatypeRepositoryTokenPassword() } } transitionCheckOptions { maxRetries.set(120) delayBetween.set(java.time.Duration.ofMillis(30000)) } } allprojects { repositories { google() mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: android-unity-wrapper/gradle/wrapper/gradle-wrapper.properties ================================================ #Tue Jan 21 12:53:02 IST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: android-unity-wrapper/gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: android-unity-wrapper/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: android-unity-wrapper/settings.gradle ================================================ rootProject.name='android-unity-wrapper' include ':app' include ':unitywrapper' ================================================ FILE: android-unity-wrapper/unitywrapper/.gitignore ================================================ /build ================================================ FILE: android-unity-wrapper/unitywrapper/build.gradle ================================================ apply plugin: 'com.android.library' android { namespace "com.appsflyer.unitywrapper" compileSdkVersion 34 defaultConfig { minSdkVersion 16 targetSdkVersion 34 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } repositories { mavenCentral() } dependencies { compileOnly fileTree(dir: 'libs', include: ['*.jar']) compileOnly 'androidx.appcompat:appcompat:1.1.0' compileOnly 'com.appsflyer:af-android-sdk:6.17.6' compileOnly 'com.appsflyer:purchase-connector:2.2.0' compileOnly 'com.android.billingclient:billing:8.0.0' } apply from: file('publish.gradle') ================================================ FILE: android-unity-wrapper/unitywrapper/consumer-rules.pro ================================================ ================================================ FILE: android-unity-wrapper/unitywrapper/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: android-unity-wrapper/unitywrapper/publish.gradle ================================================ apply plugin: 'maven-publish' apply plugin: 'signing' def isReleaseBuild() { return !VERSION_NAME.contains("-SNAPSHOT") } android { publishing { singleVariant("release") { // if you don't want sources/javadoc, remove these lines withJavadocJar() } } } afterEvaluate { publishing { publications { release(MavenPublication) { // The coordinates of the library groupId = GROUP artifactId = POM_ARTIFACT_ID version = VERSION_NAME // Artifacts to publish artifact("$buildDir/outputs/aar/${project.name}-release.aar") // Configure POM metadata pom { name.set(POM_NAME) description.set(POM_DESCRIPTION) url.set(POM_URL) licenses { license { name.set(POM_LICENCE_NAME) url.set(POM_LICENCE_URL) distribution.set(POM_LICENCE_DIST) } } developers { developer { id.set(POM_DEVELOPER_ID) name.set(POM_DEVELOPER_NAME) } } scm { connection.set(POM_SCM_CONNECTION) developerConnection.set(POM_SCM_DEV_CONNECTION) url.set(POM_SCM_URL) } } // Add dependencies to the POM pom.withXml { def dependenciesNode = asNode().appendNode('dependencies') configurations.implementation.allDependencies.each { dependency -> def dependencyNode = dependenciesNode.appendNode('dependency') dependencyNode.appendNode('groupId', dependency.group ?: '') dependencyNode.appendNode('artifactId', dependency.name) dependencyNode.appendNode('version', dependency.version ?: '') } } } } } def decodedKey = project.findProperty("signing.secretKey")?.replace('\\n', '\n') signing { // 🧪 STEP 2: Use the decoded key INSIDE the signing block if (project.hasProperty("signing.keyId") && decodedKey && project.hasProperty("signing.password")) { useInMemoryPgpKeys( project.findProperty("signing.keyId")?.toString(), decodedKey, project.findProperty("signing.password")?.toString() ) } sign publishing.publications.release } // task androidJavadocs(type: Javadoc) { // classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) // if (JavaVersion.current().isJava8Compatible()) { // tasks.withType(Javadoc) { options.addStringOption('Xdoclint:none', '-quiet') } // } // } // } // task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { // archiveClassifier = 'javadoc' // from androidJavadocs.destinationDir // } // task androidSourcesJar(type: Jar) { // classifier = 'sources' // from android.sourceSets.main.java.sourceFiles // } // artifacts { archives androidJavadocsJar } } ================================================ FILE: android-unity-wrapper/unitywrapper/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android-unity-wrapper/unitywrapper/src/main/java/com/appsflyer/unity/AppsFlyerAndroidWrapper.java ================================================ package com.appsflyer.unity; import android.annotation.SuppressLint; import android.os.Build; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.appsflyer.api.InAppPurchaseEvent; import com.appsflyer.api.PurchaseClient; import com.appsflyer.api.Store; import com.appsflyer.api.SubscriptionPurchaseEvent; import com.appsflyer.internal.models.InAppPurchaseValidationResult; import com.appsflyer.internal.models.ProductPurchase; import com.appsflyer.internal.models.SubscriptionPurchase; import com.appsflyer.internal.models.SubscriptionValidationResult; import com.appsflyer.internal.models.ValidationFailureData; import com.appsflyer.AFAdRevenueData; import com.appsflyer.MediationNetwork; import com.appsflyer.AFLogger; import com.appsflyer.AFPurchaseDetails; import com.appsflyer.AFPurchaseType; import com.appsflyer.AppsFlyerConsent; import com.appsflyer.AppsFlyerConversionListener; import com.appsflyer.AppsFlyerInAppPurchaseValidationCallback; import com.appsflyer.AppsFlyerInAppPurchaseValidatorListener; import com.appsflyer.AppsFlyerLib; import com.appsflyer.AppsFlyerProperties; import com.appsflyer.attribution.AppsFlyerRequestListener; import com.appsflyer.deeplink.DeepLinkListener; import com.appsflyer.deeplink.DeepLinkResult; import com.appsflyer.internal.platform_extension.Plugin; import com.appsflyer.internal.platform_extension.PluginInfo; import com.appsflyer.share.CrossPromotionHelper; import com.appsflyer.share.LinkGenerator; import com.appsflyer.share.ShareInviteHelper; import com.unity3d.player.UnityPlayer; import org.json.JSONObject; import java.util.HashMap; import java.util.Map; public class AppsFlyerAndroidWrapper { private static final String VALIDATE_CALLBACK = "didFinishValidateReceipt"; private static final String VALIDATE_ERROR_CALLBACK = "didFinishValidateReceiptWithError"; private static final String VALIDATE_AND_LOG_V2_CALLBACK = "onValidateAndLogComplete"; private static final String VALIDATE_AND_LOG_V2__ERROR_CALLBACK = "onValidateAndLogFailure"; private static final String GCD_CALLBACK = "onConversionDataSuccess"; private static final String GCD_ERROR_CALLBACK = "onConversionDataFail"; private static final String OAOA_CALLBACK = "onAppOpenAttribution"; private static final String OAOA_ERROR_CALLBACK = "onAppOpenAttributionFailure"; private static final String GENERATE_LINK_CALLBACK = "onInviteLinkGenerated"; private static final String GENERATE_LINK_ERROR_CALLBACK = "onInviteLinkGeneratedFailure"; private static final String ON_DEEPLINKING = "onDeepLinking"; private static final String START_REQUEST_CALLBACK = "requestResponseReceived"; private static final String IN_APP_RESPONSE_CALLBACK = "inAppResponseReceived"; private static final String VALIDATION_CALLBACK = "didReceivePurchaseRevenueValidationInfo"; private static final String ERROR_CALLBACK = "didReceivePurchaseRevenueError"; private static final String PLUGIN_VERSION = "6.17.91"; private static final long DDL_TIMEOUT_DEFAULT = 3000; private static AppsFlyerConversionListener conversionListener; private static String devkey = ""; private static long ddlTimeout = DDL_TIMEOUT_DEFAULT; private static PurchaseClient purchaseClientInstance; private static PurchaseClient.Builder builder; private static String unityObjectName; public static void initSDK(String devKey, String objectName) { if (conversionListener == null && objectName != null){ conversionListener = getConversionListener(objectName); } devkey = devKey; setPluginInfo(); AppsFlyerLib.getInstance().init(devKey, conversionListener, UnityPlayer.currentActivity); } public static void startTracking(final boolean shouldCallback, final String objectName) { AppsFlyerLib.getInstance().start(UnityPlayer.currentActivity, devkey, new AppsFlyerRequestListener() { @Override public void onSuccess() { if(shouldCallback && objectName != null){ Map map = new HashMap(); map.put("statusCode", 200); JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, START_REQUEST_CALLBACK, jsonObject.toString()); } } @Override public void onError(int i, @NonNull String s) { if(shouldCallback && objectName != null){ Map map = new HashMap(); map.put("statusCode", i); map.put("errorDescription", s); JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, START_REQUEST_CALLBACK, jsonObject.toString()); } } }); } public static void startTracking() { startTracking(false, null); } public static void stopTracking(boolean isTrackingStopped) { AppsFlyerLib.getInstance().stop(isTrackingStopped, UnityPlayer.currentActivity); } public static String getSdkVersion() { return AppsFlyerLib.getInstance().getSdkVersion(); } public static void updateServerUninstallToken(String token) { AppsFlyerLib.getInstance().updateServerUninstallToken(UnityPlayer.currentActivity, token); } public static void setIsDebug(boolean shouldEnable) { AppsFlyerLib.getInstance().setDebugLog(shouldEnable); } public static void setImeiData(String aImei) { AppsFlyerLib.getInstance().setImeiData(aImei); } public static void setAndroidIdData(String aAndroidId) { AppsFlyerLib.getInstance().setAndroidIdData(aAndroidId); } public static void setCustomerUserId(String id) { AppsFlyerLib.getInstance().setCustomerUserId(id); } public static void waitForCustomerUserId(boolean wait) { AppsFlyerLib.getInstance().waitForCustomerUserId(true); } public static void setCustomerIdAndTrack(String id) { AppsFlyerLib.getInstance().setCustomerIdAndLogSession(id, UnityPlayer.currentActivity); } public static void enableTCFDataCollection(boolean shouldCollectTcfData) { AppsFlyerLib.getInstance().enableTCFDataCollection(shouldCollectTcfData); } public static void setConsentData(String isUserSubjectToGDPR, String hasConsentForDataUsage, String hasConsentForAdsPersonalization, String hasConsentForAdStorage) { Boolean gdprApplies = parseNullableBoolean(isUserSubjectToGDPR); Boolean dataUsage = parseNullableBoolean(hasConsentForDataUsage); Boolean adsPersonalization = parseNullableBoolean(hasConsentForAdsPersonalization); Boolean adStorage = parseNullableBoolean(hasConsentForAdStorage); AppsFlyerLib.getInstance().setConsentData(new AppsFlyerConsent(gdprApplies, dataUsage, adsPersonalization, adStorage)); } public static void logAdRevenue(String monetizationNetwork, MediationNetwork mediationNetwork, String currencyIso4217Code, double revenue, HashMap additionalParameters) { AFAdRevenueData adRevenueData = new AFAdRevenueData(monetizationNetwork, mediationNetwork, currencyIso4217Code, revenue); AppsFlyerLib.getInstance().logAdRevenue(adRevenueData, additionalParameters); } public static String getOutOfStore() { return AppsFlyerLib.getInstance().getOutOfStore(UnityPlayer.currentActivity); } public static void setOutOfStore(String sourceName) { AppsFlyerLib.getInstance().setOutOfStore(sourceName); } public static void setAppInviteOneLinkID(String oneLinkId) { AppsFlyerLib.getInstance().setAppInviteOneLink(oneLinkId); } public static void setAdditionalData(HashMap customData) { AppsFlyerLib.getInstance().setAdditionalData(customData); } public static void setUserEmails(String... emails) { AppsFlyerLib.getInstance().setUserEmails(emails); } public static void setUserEmails(AppsFlyerProperties.EmailsCryptType cryptMethod, String... emails) { AppsFlyerLib.getInstance().setUserEmails(cryptMethod, emails); } public static void setCollectAndroidID(boolean isCollect) { AppsFlyerLib.getInstance().setCollectAndroidID(isCollect); } public static void setCollectIMEI(boolean isCollect) { AppsFlyerLib.getInstance().setCollectIMEI(isCollect); } public static void setResolveDeepLinkURLs(String... urls) { AppsFlyerLib.getInstance().setResolveDeepLinkURLs(urls); } public static void setOneLinkCustomDomain(String... domains) { AppsFlyerLib.getInstance().setOneLinkCustomDomain(domains); } public static void setIsUpdate(boolean isUpdate) { AppsFlyerLib.getInstance().setIsUpdate(isUpdate); } public static void setCurrencyCode(String currencyCode) { AppsFlyerLib.getInstance().setCurrencyCode(currencyCode); } public static void trackLocation(double latitude, double longitude) { AppsFlyerLib.getInstance().logLocation(UnityPlayer.currentActivity, latitude, longitude); } public static void trackEvent(String eventName, HashMap eventValues, final boolean shouldCallback, final String objectName) { AppsFlyerLib.getInstance().logEvent(UnityPlayer.currentActivity, eventName, eventValues, new AppsFlyerRequestListener() { @Override public void onSuccess() { if(shouldCallback && objectName != null){ Map map = new HashMap(); map.put("statusCode", 200); JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, IN_APP_RESPONSE_CALLBACK, jsonObject.toString()); } } @Override public void onError(int i, @NonNull String s) { if(shouldCallback && objectName != null){ Map map = new HashMap(); map.put("statusCode", i); map.put("errorDescription", s); JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, IN_APP_RESPONSE_CALLBACK, jsonObject.toString()); } } }); } public static void trackEvent(String eventName, HashMap eventValues) { trackEvent(eventName, eventValues, false, null); } public static void setDeviceTrackingDisabled(boolean isDisabled) { AppsFlyerLib.getInstance().anonymizeUser(isDisabled); } public static void enableFacebookDeferredApplinks(boolean isEnabled) { AppsFlyerLib.getInstance().enableFacebookDeferredApplinks(isEnabled); } public static void setConsumeAFDeepLinks(boolean doConsume) { //AppsFlyerLib.getInstance().setConsumeAFDeepLinks(doConsume); } public static void setPreinstallAttribution(String mediaSource, String campaign, String siteId) { AppsFlyerLib.getInstance().setPreinstallAttribution(mediaSource, campaign, siteId); } public static boolean isPreInstalledApp() { return AppsFlyerLib.getInstance().isPreInstalledApp(UnityPlayer.currentActivity); } public static String getAttributionId() { return AppsFlyerLib.getInstance().getAttributionId(UnityPlayer.currentActivity); } public static String getAppsFlyerId() { return AppsFlyerLib.getInstance().getAppsFlyerUID(UnityPlayer.currentActivity); } public static void validateAndTrackInAppPurchase(String publicKey, String signature, String purchaseData, String price, String currency, HashMap additionalParameters, String objectName) { AppsFlyerLib.getInstance().validateAndLogInAppPurchase(UnityPlayer.currentActivity, publicKey, signature, purchaseData, price, currency, additionalParameters); if (objectName != null){ initInAppPurchaseValidatorListener(objectName); } } public static void validateAndTrackInAppPurchaseV2(int purchaseType, String purchaseToken, String productId, HashMap purchaseAdditionalDetails, final String objectName) { AFPurchaseType type = purchaseType == 0 ? AFPurchaseType.SUBSCRIPTION : AFPurchaseType.ONE_TIME_PURCHASE; AFPurchaseDetails details = new AFPurchaseDetails(type, purchaseToken, productId); if (objectName != null){ AppsFlyerInAppPurchaseValidationCallback listener = initInAppPurchaseValidatorV2Listener(objectName); AppsFlyerLib.getInstance().validateAndLogInAppPurchase(details, purchaseAdditionalDetails, listener); } } public static boolean isTrackingStopped() { return AppsFlyerLib.getInstance().isStopped(); } public static void setMinTimeBetweenSessions(int seconds) { AppsFlyerLib.getInstance().setMinTimeBetweenSessions(seconds); } public static void setLogLevel(AFLogger.LogLevel logLevel) { AppsFlyerLib.getInstance().setLogLevel(logLevel); } public static void setHost(String hostPrefixName, String hostName) { AppsFlyerLib.getInstance().setHost(hostPrefixName, hostName); } public static String getHostName() { return AppsFlyerLib.getInstance().getHostName(); } public static String getHostPrefix() { return AppsFlyerLib.getInstance().getHostPrefix(); } public static void setCollectOaid(boolean isCollect) { AppsFlyerLib.getInstance().setCollectOaid(isCollect); } public static void setSharingFilterForAllPartners() { AppsFlyerLib.getInstance().setSharingFilterForAllPartners(); } public static void setSharingFilter(String ... partners) { AppsFlyerLib.getInstance().setSharingFilter(partners); } public static void getConversionData(final String objectName){ if (conversionListener == null){ conversionListener = getConversionListener(objectName); } AppsFlyerLib.getInstance().registerConversionListener(UnityPlayer.currentActivity, conversionListener); } private static AppsFlyerConversionListener getConversionListener(final String objectName){ return new AppsFlyerConversionListener() { @Override public void onConversionDataSuccess(Map map) { if(objectName != null){ JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, GCD_CALLBACK, jsonObject.toString()); } } @Override public void onConversionDataFail(String s) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, GCD_ERROR_CALLBACK, s); } } @Override public void onAppOpenAttribution(Map map) { if(objectName != null){ JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, OAOA_CALLBACK, jsonObject.toString()); } } @Override public void onAttributionFailure(String s) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, OAOA_ERROR_CALLBACK, s); } } }; } private static Boolean parseNullableBoolean(String value) { if (value == null) return null; if (value.equalsIgnoreCase("true")) return true; if (value.equalsIgnoreCase("false")) return false; return null; } public static void initInAppPurchaseValidatorListener(final String objectName) { AppsFlyerLib.getInstance().registerValidatorListener(UnityPlayer.currentActivity, new AppsFlyerInAppPurchaseValidatorListener() { @Override public void onValidateInApp() { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, VALIDATE_CALLBACK, "Validate success"); } } @Override public void onValidateInAppFailure(String error) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, VALIDATE_ERROR_CALLBACK, error); } } }); } public static AppsFlyerInAppPurchaseValidationCallback initInAppPurchaseValidatorV2Listener(final String objectName) { return new AppsFlyerInAppPurchaseValidationCallback() { @Override public void onInAppPurchaseValidationFinished(@NonNull Map map) { if (objectName != null) { JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, VALIDATE_AND_LOG_V2_CALLBACK, jsonObject.toString()); } } @Override public void onInAppPurchaseValidationError(@NonNull Map map) { if (objectName != null) { JSONObject jsonObject = new JSONObject(map); UnityPlayer.UnitySendMessage(objectName, VALIDATE_AND_LOG_V2__ERROR_CALLBACK, jsonObject.toString()); } } }; } public static void handlePushNotifications(){ AppsFlyerLib.getInstance().sendPushNotificationData(UnityPlayer.currentActivity); } public static void setPhoneNumber(String phoneNumber){ AppsFlyerLib.getInstance().setPhoneNumber(phoneNumber); } public static void attributeAndOpenStore(String promoted_app_id, String campaign, Map userParams) { CrossPromotionHelper.logAndOpenStore(UnityPlayer.currentActivity, promoted_app_id, campaign, userParams); } public static void recordCrossPromoteImpression(String appID, String campaign, Map params){ CrossPromotionHelper.logCrossPromoteImpression(UnityPlayer.currentActivity, appID, campaign, params); } public static void createOneLinkInviteListener(Map params, final String objectName){ LinkGenerator linkGenerator = ShareInviteHelper.generateInviteUrl(UnityPlayer.currentActivity); linkGenerator.setChannel(params.get("channel")); linkGenerator.setCampaign(params.get("campaign")); linkGenerator.setReferrerName(params.get("referrerName")); linkGenerator.setReferrerImageURL(params.get("referrerImageUrl")); linkGenerator.setReferrerCustomerId(params.get("customerID")); linkGenerator.setBaseDeeplink(params.get("baseDeepLink")); linkGenerator.setBrandDomain(params.get("brandDomain")); params.remove("channel"); params.remove("campaign"); params.remove("referrerName"); params.remove("referrerImageUrl"); params.remove("customerID"); params.remove("baseDeepLink"); params.remove("brandDomain"); linkGenerator.addParameters(params); linkGenerator.generateLink(UnityPlayer.currentActivity, new LinkGenerator.ResponseListener() { @Override public void onResponse(String link) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, GENERATE_LINK_CALLBACK, link); } } @Override public void onResponseError(String error) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, GENERATE_LINK_ERROR_CALLBACK, error); } } }); } public static void subscribeForDeepLink(final String objectName){ if (ddlTimeout != DDL_TIMEOUT_DEFAULT) { AppsFlyerLib.getInstance().subscribeForDeepLink(new DeepLinkListener() { @Override public void onDeepLinking(@NonNull DeepLinkResult deepLinkResult) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, ON_DEEPLINKING, deepLinkResult.toString()); } } }, ddlTimeout); } else { AppsFlyerLib.getInstance().subscribeForDeepLink(new DeepLinkListener() { @Override public void onDeepLinking(@NonNull DeepLinkResult deepLinkResult) { if(objectName != null){ UnityPlayer.UnitySendMessage(objectName, ON_DEEPLINKING, deepLinkResult.toString()); } } }); } } public static void addPushNotificationDeepLinkPath(String ... path){ AppsFlyerLib.getInstance().addPushNotificationDeepLinkPath(path); } public static void setDisableAdvertisingIdentifiers(boolean disable){ AppsFlyerLib.getInstance().setDisableAdvertisingIdentifiers(disable); } public static void setSharingFilterForPartners(java.lang.String... partners){ AppsFlyerLib.getInstance().setSharingFilterForPartners(partners); } public static void setDisableNetworkData(boolean disable){ AppsFlyerLib.getInstance().setDisableNetworkData(disable); } public static void setPluginInfo() { PluginInfo pluginInfo = new PluginInfo(Plugin.UNITY, PLUGIN_VERSION); AppsFlyerLib.getInstance().setPluginInfo(pluginInfo); } public static void setDeepLinkTimeout(long deepLinkTimeout) { ddlTimeout = deepLinkTimeout; } //Purchase Connector public static void initPurchaseConnector(String objectName, int store) { unityObjectName = objectName; Store s = mappingEnum(store); if (s != null) { builder = new PurchaseClient.Builder(UnityPlayer.currentActivity, s); builder = PurchaseRevenueBridge.configurePurchaseClient(builder); } else { Log.w("AppsFlyer_Connector", "[PurchaseConnector]: Please choose a valid store."); } } public static void build() { if (builder != null) { purchaseClientInstance = builder.build(); } else { Log.w("AppsFlyer_Connector", "[PurchaseConnector]: Initialization is required prior to building."); } } public static void setIsSandbox(boolean isSandbox) { if (builder != null) { builder.setSandbox(isSandbox); } } public static void setAutoLogSubscriptions(boolean logSubscriptions) { if (builder != null) { builder.logSubscriptions(logSubscriptions); } } public static void setAutoLogInApps(boolean autoLogInApps) { if (builder != null) { builder.autoLogInApps(autoLogInApps); } } public static void setPurchaseRevenueValidationListeners(boolean enableCallbacks) { if (builder != null && enableCallbacks) { builder.setSubscriptionValidationResultListener(new PurchaseClient.SubscriptionPurchaseValidationResultListener() { @RequiresApi(api = Build.VERSION_CODES.N) @Override public void onResponse(@Nullable Map result) { if (unityObjectName != null) { if (result == null) { return; } result.forEach((k, v) -> { Map map = new HashMap<>(); Map mapSubscription = new HashMap<>(); map.put("productId", k); map.put("success", v.getSuccess() ? "true" : "false"); if (v.getSuccess()) { SubscriptionPurchase subscriptionPurchase = v.getSubscriptionPurchase(); Map mapCancelSurveyResult = new HashMap<>(); if (subscriptionPurchase.getCanceledStateContext() != null) { Map mapCanceledStateContext = new HashMap<>(); Map mapUserInitiatedCancellation = new HashMap<>(); if (subscriptionPurchase.getCanceledStateContext(). getUserInitiatedCancellation() != null) { if (subscriptionPurchase.getCanceledStateContext(). getUserInitiatedCancellation(). getCancelSurveyResult() != null) { mapCancelSurveyResult.put("reason", subscriptionPurchase.getCanceledStateContext(). getUserInitiatedCancellation(). getCancelSurveyResult().getReason()); mapCancelSurveyResult.put("reasonUserInput", subscriptionPurchase.getCanceledStateContext(). getUserInitiatedCancellation(). getCancelSurveyResult().getReasonUserInput()); mapUserInitiatedCancellation.put("cancelSurveyResult", mapCancelSurveyResult); } mapUserInitiatedCancellation.put("cancelTime", subscriptionPurchase.getCanceledStateContext(). getUserInitiatedCancellation().getCancelTime()); } mapCanceledStateContext.put("developerInitiatedCancellation", null); mapCanceledStateContext.put("replacementCancellation", null); mapCanceledStateContext.put("systemInitiatedCancellation", null); mapCanceledStateContext.put("userInitiatedCancellation", mapUserInitiatedCancellation); } if (subscriptionPurchase.getExternalAccountIdentifiers() != null) { Map mapExternalAccountIdentifiers = new HashMap<>(); mapExternalAccountIdentifiers.put("externalAccountId", subscriptionPurchase.getExternalAccountIdentifiers(). getExternalAccountId()); mapExternalAccountIdentifiers.put("obfuscatedExternalAccountId", subscriptionPurchase.getExternalAccountIdentifiers(). getObfuscatedExternalAccountId()); mapExternalAccountIdentifiers.put("obfuscatedExternalProfileId", subscriptionPurchase.getExternalAccountIdentifiers(). getObfuscatedExternalProfileId()); mapSubscription.put("externalAccountIdentifiers", mapExternalAccountIdentifiers); } if (subscriptionPurchase.getPausedStateContext() != null) { Map mapPausedStateContext = new HashMap<>(); mapPausedStateContext.put("autoResumeTime", subscriptionPurchase.getPausedStateContext(). getAutoResumeTime()); mapSubscription.put("pausedStateContext", mapPausedStateContext); } if (subscriptionPurchase.getSubscribeWithGoogleInfo() != null) { Map mapSubscribeWithGoogleInfo = new HashMap<>(); mapSubscribeWithGoogleInfo.put("emailAddress", subscriptionPurchase.getSubscribeWithGoogleInfo().getEmailAddress()); mapSubscribeWithGoogleInfo.put("familyName", subscriptionPurchase.getSubscribeWithGoogleInfo().getFamilyName()); mapSubscribeWithGoogleInfo.put("givenName", subscriptionPurchase.getSubscribeWithGoogleInfo().getGivenName()); mapSubscribeWithGoogleInfo.put("profileId", subscriptionPurchase.getSubscribeWithGoogleInfo().getProfileId()); mapSubscribeWithGoogleInfo.put("profileName", subscriptionPurchase.getSubscribeWithGoogleInfo().getProfileName()); mapSubscription.put("subscribeWithGoogleInfo", mapSubscribeWithGoogleInfo); } int sizeItems = subscriptionPurchase.getLineItems().size(); Map[] lineItems = new Map[sizeItems]; for (int i = 0; i < sizeItems; i++) { Map mapSubscriptionPurchaseLineItem = new HashMap<>(); mapSubscriptionPurchaseLineItem.put("expiryTime", subscriptionPurchase.getLineItems().get(i).getExpiryTime()); mapSubscriptionPurchaseLineItem.put("productId", subscriptionPurchase.getLineItems().get(i).getProductId()); if (subscriptionPurchase.getLineItems().get(i).getAutoRenewingPlan() != null) { Map mapAutoRenewingPlan = new HashMap<>(); if (subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan().getAutoRenewEnabled() != null) { mapAutoRenewingPlan.put("autoRenewEnabled", subscriptionPurchase.getLineItems().get(i).getAutoRenewingPlan(). getAutoRenewEnabled() ? "true" : "false"); } if (subscriptionPurchase.getLineItems().get(i).getAutoRenewingPlan(). getPriceChangeDetails() != null) { Map mapPriceChangeDetails = new HashMap<>(); mapPriceChangeDetails.put("expectedNewPriceChargeTime", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan().getPriceChangeDetails(). getExpectedNewPriceChargeTime()); mapPriceChangeDetails.put("priceChangeMode", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan(). getPriceChangeDetails().getPriceChangeMode()); mapPriceChangeDetails.put("priceChangeState", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan(). getPriceChangeDetails().getPriceChangeState()); mapAutoRenewingPlan.put("priceChangeDetails", mapPriceChangeDetails); if (subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan().getPriceChangeDetails(). getNewPrice() != null) { Map mapMoney = new HashMap<>(); mapMoney.put("currencyCode", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan(). getPriceChangeDetails().getNewPrice(). getCurrencyCode()); mapMoney.put("nanos", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan(). getPriceChangeDetails().getNewPrice().getNanos()); mapMoney.put("units", subscriptionPurchase.getLineItems().get(i). getAutoRenewingPlan(). getPriceChangeDetails().getNewPrice().getUnits()); mapPriceChangeDetails.put("newPrice", mapMoney); } } mapSubscriptionPurchaseLineItem.put("autoRenewingPlan", mapAutoRenewingPlan); } if (subscriptionPurchase.getLineItems().get(i).getOfferDetails() != null) { Map mapOfferDetails = new HashMap<>(); mapOfferDetails.put("basePlanId", subscriptionPurchase.getLineItems().get(i). getOfferDetails().getBasePlanId()); if (subscriptionPurchase.getLineItems().get(i). getOfferDetails().getOfferId() != null) { mapOfferDetails.put("offerId", subscriptionPurchase.getLineItems().get(i). getOfferDetails().getOfferId()); } mapSubscriptionPurchaseLineItem.put("offerDetails", mapOfferDetails); } if (subscriptionPurchase.getLineItems().get(i).getDeferredItemReplacement() != null) { Map mapDeferredItemReplacement = new HashMap<>(); mapDeferredItemReplacement.put("productId", subscriptionPurchase.getLineItems().get(i). getDeferredItemReplacement().getProductId()); mapSubscriptionPurchaseLineItem.put("deferredItemReplacement", mapDeferredItemReplacement); } if (subscriptionPurchase.getLineItems().get(i).getPrepaidPlan() != null && subscriptionPurchase.getLineItems().get(i). getPrepaidPlan().getAllowExtendAfterTime() != null) { Map mapPrepaidPlan = new HashMap<>(); mapPrepaidPlan.put("allowExtendAfterTime", subscriptionPurchase.getLineItems().get(i). getPrepaidPlan().getAllowExtendAfterTime()); mapSubscriptionPurchaseLineItem.put("prepaidPlan", mapPrepaidPlan); } lineItems[i] = mapSubscriptionPurchaseLineItem; } mapSubscription.put("lineItems", lineItems); mapSubscription.put("acknowledgementState", subscriptionPurchase.getAcknowledgementState()); mapSubscription.put("canceledStateContext", subscriptionPurchase.getCanceledStateContext()); mapSubscription.put("kind", subscriptionPurchase.getKind()); mapSubscription.put("latestOrderId", subscriptionPurchase.getLatestOrderId()); mapSubscription.put("linkedPurchaseToken", subscriptionPurchase.getLinkedPurchaseToken()); mapSubscription.put("regionCode", subscriptionPurchase.getRegionCode()); mapSubscription.put("subscriptionState", subscriptionPurchase.getSubscriptionState()); mapSubscription.put("testPurchase", null); mapSubscription.put("startTime", subscriptionPurchase.getStartTime()); map.put("subscriptionPurchase", mapSubscription); } else { ValidationFailureData failureData = v.getFailureData(); Map mapValidationFailureData = new HashMap<>(); mapValidationFailureData.put("status", failureData.getStatus()); mapValidationFailureData.put("description", failureData.getDescription()); map.put("failureData", mapValidationFailureData); } JSONObject resultObject = new JSONObject(map); UnityPlayer.UnitySendMessage(unityObjectName, VALIDATION_CALLBACK, resultObject.toString()); }); } } @Override public void onFailure(@NonNull String result, @Nullable Throwable error) { if (unityObjectName != null) { UnityPlayer.UnitySendMessage(unityObjectName, ERROR_CALLBACK, result); } } }); builder.setInAppValidationResultListener(new PurchaseClient.InAppPurchaseValidationResultListener() { @SuppressLint("LongLogTag") @RequiresApi(api = Build.VERSION_CODES.N) @Override public void onResponse(@Nullable Map result) { if (unityObjectName != null) { if (result == null) { return; } result.forEach((k, v) -> { Map map = new HashMap<>(); Map mapIAP = new HashMap<>(); // JSONObject jsonObject = new JSONObject(map); map.put("token", k); map.put("success", v.getSuccess() ? "true" : "false"); if (v.getSuccess()) { ProductPurchase productPurchase = v.getProductPurchase(); mapIAP.put("productId", productPurchase.getProductId()); mapIAP.put("purchaseState", productPurchase.getPurchaseState()); mapIAP.put("kind", productPurchase.getKind()); mapIAP.put("purchaseTimeMillis", productPurchase.getPurchaseTimeMillis()); mapIAP.put("consumptionState", productPurchase.getConsumptionState()); mapIAP.put("developerPayload", productPurchase.getDeveloperPayload()); mapIAP.put("orderId", productPurchase.getOrderId()); mapIAP.put("purchaseType", productPurchase.getPurchaseType()); mapIAP.put("acknowledgementState", productPurchase.getAcknowledgementState()); mapIAP.put("purchaseToken", productPurchase.getPurchaseToken()); mapIAP.put("quantity", productPurchase.getQuantity()); mapIAP.put("obfuscatedExternalAccountId", productPurchase.getObfuscatedExternalAccountId()); mapIAP.put("obfuscatedExternalProfileId", productPurchase.getObfuscatedExternalProfileId()); mapIAP.put("regionCode", productPurchase.getRegionCode()); map.put("productPurchase", mapIAP); } else { ValidationFailureData failureData = v.getFailureData(); Map mapValidationFailureData = new HashMap<>(); mapValidationFailureData.put("status", failureData.getStatus()); mapValidationFailureData.put("description", failureData.getDescription()); map.put("failureData", mapValidationFailureData); } JSONObject resultObject = new JSONObject(map); UnityPlayer.UnitySendMessage(unityObjectName, VALIDATION_CALLBACK, resultObject.toString()); }); } } @Override public void onFailure(@NonNull String result, @Nullable Throwable error) { if (unityObjectName != null) { UnityPlayer.UnitySendMessage(unityObjectName, ERROR_CALLBACK, result); } } }); } } public static void startObservingTransactions() { if (purchaseClientInstance != null) { purchaseClientInstance.startObservingTransactions(); } else { Log.w("AppsFlyer_Connector", "[PurchaseConnector]: startObservingTransactions was not called because the purchase client instance is null, please call build() prior to this function."); } } public static void stopObservingTransactions() { if (purchaseClientInstance != null) { purchaseClientInstance.stopObservingTransactions(); } else { Log.w("AppsFlyer_Connector", "[PurchaseConnector]: stopObservingTransactions was not called because the purchase client instance is null, please call build() prior to this function."); } } private static Store mappingEnum(int storeEnum) { switch (storeEnum) { case 0: return Store.GOOGLE; default: return null; } } } ================================================ FILE: android-unity-wrapper/unitywrapper/src/main/java/com/appsflyer/unity/PurchaseRevenueBridge.java ================================================ package com.appsflyer.unity; import android.util.Log; import com.appsflyer.api.InAppPurchaseEvent; import com.appsflyer.api.SubscriptionPurchaseEvent; import com.appsflyer.api.PurchaseClient; import com.appsflyer.api.Store; import com.appsflyer.internal.models.InAppPurchaseValidationResult; import com.appsflyer.internal.models.SubscriptionValidationResult; import com.appsflyer.internal.models.ValidationFailureData; import com.unity3d.player.UnityPlayer; import org.json.JSONObject; import org.json.JSONException; import org.json.JSONArray; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Iterator; public class PurchaseRevenueBridge { private static final String TAG = "AppsFlyerUnity"; public interface UnityPurchaseRevenueBridge { String getAdditionalParameters(String productsJson, String transactionsJson); } private static UnityPurchaseRevenueBridge unityBridge; public static void setUnityBridge(UnityPurchaseRevenueBridge bridge) { unityBridge = bridge; } public static PurchaseClient.Builder configurePurchaseClient(PurchaseClient.Builder builder) { return builder .setInAppPurchaseEventDataSource(purchaseEvents -> { try { String eventsJson = new JSONObject(Collections.singletonMap("events", purchaseEvents)).toString(); String response = unityBridge != null ? unityBridge.getAdditionalParameters(eventsJson, "") : null; if (response != null) { JSONObject json = new JSONObject(response); Map map = new HashMap<>(); Iterator keys = json.keys(); while (keys.hasNext()) { String key = keys.next(); map.put(key, json.get(key)); } return map; } } catch (JSONException e) { Log.e(TAG, "Failed to parse additional params from Unity", e); } return Collections.emptyMap(); }) .setSubscriptionPurchaseEventDataSource(purchaseEvents -> { try { String eventsJson = new JSONObject(Collections.singletonMap("events", purchaseEvents)).toString(); String response = unityBridge != null ? unityBridge.getAdditionalParameters("", eventsJson) : null; if (response != null) { JSONObject json = new JSONObject(response); Map map = new HashMap<>(); Iterator keys = json.keys(); while (keys.hasNext()) { String key = keys.next(); map.put(key, json.get(key)); } return map; } } catch (JSONException e) { Log.e(TAG, "Failed to parse additional params from Unity", e); } return Collections.emptyMap(); }); } } ================================================ FILE: deploy/build_unity_package.sh ================================================ #!/bin/bash echo "Start Build for appsflyer-unity-plugin.unitypackage" DEPLOY_PATH=outputs UNITY_PATH="/Applications/Unity/Unity.app/Contents/MacOS/Unity" PACKAGE_NAME="appsflyer-unity-plugin-6.17.91.unitypackage" mkdir -p $DEPLOY_PATH #move external dependency manager echo "moving the external dependency manager to root" mv external-dependency-manager-1.2.183.unitypackage .. # Temporarily move Tests folder to avoid NUnit compilation errors in batch mode echo "Temporarily moving Tests folder..." rm -rf ../Tests_temp ../Tests_temp.meta mv ../Assets/AppsFlyer/Tests ../Tests_temp mv ../Assets/AppsFlyer/Tests.meta ../Tests_temp.meta 2>/dev/null || true # Build the .unitypackage /Applications/Unity/Hub/Editor/6000.3.1f1/Unity.app/Contents/MacOS/Unity \ -gvh_disable \ -batchmode \ -importPackage external-dependency-manager-1.2.183.unitypackage \ -nographics \ -logFile create_unity_core.log \ -projectPath $PWD/../ \ -exportPackage \ Assets/AppsFlyer \ $PWD/$DEPLOY_PATH/$PACKAGE_NAME \ -quit \ && echo "package exported successfully to outputs/appsflyer-unity-plugin-6.17.91.unitypackage" \ || echo "Failed to export package. See create_unity_core.log for more info." # Move Tests folder back echo "Moving Tests folder back..." mv ../Tests_temp ../Assets/AppsFlyer/Tests mv ../Tests_temp.meta ../Assets/AppsFlyer/Tests.meta 2>/dev/null || true if [ "$1" == "-p" ]; then echo "moving back the external dependency manager to deploy" mv ../external-dependency-manager-1.2.183.unitypackage . echo "removing ./Library" rm -rf ../Library echo "removing ./Logs" rm -rf ../Logs echo "removing ./Packages" rm -rf ../Packages echo "removing ./deploy/create_unity_core.log" rm ./create_unity_core.log echo "Moving $DEPLOY_PATH/$PACKAGE_NAME to root" mv ./outputs/$PACKAGE_NAME .. echo "removing ./deploy/outputs" rm -rf ./outputs echo "removing ./Assets extra files" rm -rf ../Assets/ExternalDependencyManager rm -rf ../Assets/PlayServicesResolver rm ../Assets/ExternalDependencyManager.meta rm ../Assets/PlayServicesResolver.meta else echo "dev mode. No files removed. Run with -p flag for production build." fi ================================================ FILE: deploy/strict_mode_build_package.sh ================================================ #!/bin/bash echo "Start Build for appsflyer-unity-plugin.unitypackage. Strict Mode." DEPLOY_PATH=outputs UNITY_PATH="/Applications/Unity/Unity.app/Contents/MacOS/Unity" PACKAGE_NAME="appsflyer-unity-plugin-strict-mode-6.17.91.unitypackage" mkdir -p $DEPLOY_PATH #move external dependency manager echo "moving the external dependency manager to root" mv external-dependency-manager-1.2.183.unitypackage .. echo "Changing AppsFlyerFramework to Strict Mode" sed -i '' 's/AppsFlyerFramework/AppsFlyerFramework\/Strict/g' ../Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml echo "Changing AppsFlyerFramework to Strict Mode. Done." echo "Changing PurchaseConnector to Strict Mode" sed -i '' 's/PurchaseConnector/PurchaseConnector\/Strict/g' ../Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml echo "Changing PurchaseConnector to Strict Mode. Done." echo "Commenting out disableAdvertisingIdentifier" sed -i '' 's/\[AppsFlyerLib shared\].disableAdvertisingIdentifier/\/\/\[AppsFlyerLib shared\].disableAdvertisingIdentifier/g' ../Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm echo "Commenting out waitForATTUserAuthorizationWithTimeoutInterval" sed -i '' 's/\[\[AppsFlyerLib shared\] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval\];/\/\/\[\[AppsFlyerLib shared\] waitForATTUserAuthorizationWithTimeoutInterval:timeoutInterval\];/g' ../Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm echo "Commenting out functions. Done." # Temporarily move Tests folder to avoid NUnit compilation errors in batch mode echo "Temporarily moving Tests folder..." rm -rf ../Tests_temp ../Tests_temp.meta mv ../Assets/AppsFlyer/Tests ../Tests_temp mv ../Assets/AppsFlyer/Tests.meta ../Tests_temp.meta 2>/dev/null || true # Build the .unitypackage /Applications/Unity/Hub/Editor/6000.3.1f1/Unity.app/Contents/MacOS/Unity \ -gvh_disable \ -batchmode \ -importPackage external-dependency-manager-1.2.183.unitypackage \ -nographics \ -logFile create_unity_core.log \ -projectPath $PWD/../ \ -exportPackage \ Assets/AppsFlyer \ $PWD/$DEPLOY_PATH/$PACKAGE_NAME \ -quit \ && echo "package exported successfully to outputs/appsflyer-unity-plugin-strict-mode-6.17.91.unitypackage" \ || echo "Failed to export package. See create_unity_core.log for more info." # Move Tests folder back echo "Moving Tests folder back..." mv ../Tests_temp ../Assets/AppsFlyer/Tests mv ../Tests_temp.meta ../Assets/AppsFlyer/Tests.meta 2>/dev/null || true if [ "$1" == "-p" ]; then echo "moving back the external dependency manager to deploy" mv ../external-dependency-manager-1.2.183.unitypackage . echo "removing ./Library" rm -rf ../Library echo "removing ./Logs" rm -rf ../Logs echo "removing ./Packages" rm -rf ../Packages echo "removing ./deploy/create_unity_core.log" rm ./create_unity_core.log echo "Moving $DEPLOY_PATH/$PACKAGE_NAME to strict-mode-sdk folder" mv ./outputs/$PACKAGE_NAME ../strict-mode-sdk echo "removing ./deploy/outputs" rm -rf ./outputs echo "removing ./Assets extra files" rm -rf ../Assets/ExternalDependencyManager rm -rf ../Assets/PlayServicesResolver rm ../Assets/ExternalDependencyManager.meta rm ../Assets/PlayServicesResolver.meta echo "Uncomment disableAdvertisingIdentifier" sed -i '' 's/\/\/\[AppsFlyerLib/\[AppsFlyerLib/g' ../Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm echo "Uncomment waitForATTUserAuthorizationWithTimeoutInterval" sed -i '' 's/\/\/\[\[AppsFlyerLib/\[\[AppsFlyerLib/g' ../Assets/AppsFlyer/Plugins/iOS/AppsFlyeriOSWrapper.mm echo "Uncomment functions. Done." echo "Changing AppsFlyerFramework back" sed -i '' 's/AppsFlyerFramework\/Strict/AppsFlyerFramework/g' ../Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml echo "Changing AppsFlyerFramework back. Done." echo "Changing PurchaseConnector back" sed -i '' 's/PurchaseConnector\/Strict/PurchaseConnector/g' ../Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml echo "Changing PurchaseConnector back. Done." else echo "dev mode. No files removed. Run with -p flag for production build." fi ================================================ FILE: docs/API.md ================================================ --- title: API reference category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 12 hidden: false --- The list of available methods for this plugin is described below. - [Android, iOS and Windows API](#android-ios-and-windows-api) - [initSDK](#initsdk) - [startSDK](#startsdk) - [Android and iOS API](#android-and-ios-api) - [stopSDK](#stopsdk) - [isSDKStopped](#issdkstopped) - [getSdkVersion](#getsdkversion) - [setIsDebug](#setisdebug) - [addPushNotificationDeepLinkPath](#addpushnotificationdeeplinkpath) - [setCustomerUserId](#setcustomeruserid) - [setAppInviteOneLinkID](#setappinviteonelinkid) - [setAdditionalData](#setadditionaldata) - [setResolveDeepLinkURLs](#setresolvedeeplinkurls) - [setOneLinkCustomDomain](#setonelinkcustomdomain) - [setCurrencyCode](#setcurrencycode) - [setDeepLinkTimeout](#setdeeplinktimeout) - [enableTCFDataCollection](#enabletcfdatacollection) - [setConsentData](#setconsentdata) - [recordLocation](#recordlocation) - [anonymizeUser](#anonymizeuser) - [getAppsFlyerId](#getappsflyerid) - [setMinTimeBetweenSessions](#setmintimebetweensessions) - [setHost](#sethost) - [setUserEmails *Soon to be deprecated*](#setuseremails-soon-to-be-deprecated) - [setPhoneNumber *Soon to be deprecated*](#setphonenumber-soon-to-be-deprecated) - [getConversionData](#getconversiondata) - [attributeAndOpenStore](#attributeandopenstore) - [logAdRevenue](#logadrevenue) - [recordCrossPromoteImpression](#recordcrosspromoteimpression) - [generateUserInviteLink](#generateuserinvitelink) - [setSharingFilterForAllPartners *Deprecated*](#setsharingfilterforallpartners-deprecated) - [setSharingFilter *Deprecated*](#setsharingfilter-deprecated) - [setSharingFilterForPartners](#setsharingfilterforpartners) - [setPartnerData](#setpartnerdata) - [Android Only API](#android-only-api) - [updateServerUninstallToken](#updateserveruninstalltoken) - [setImeiData](#setimeidata) - [setAndroidIdData](#setandroididdata) - [waitForCustomerUserId](#waitforcustomeruserid) - [setCustomerIdAndStartSDK](#setcustomeridandstartsdk) - [getOutOfStore](#getoutofstore) - [setOutOfStore](#setoutofstore) - [setCollectAndroidID](#setcollectandroidid) - [setCollectIMEI](#setcollectimei) - [setIsUpdate](#setisupdate) - [setPreinstallAttribution](#setpreinstallattribution) - [isPreInstalledApp](#ispreinstalledapp) - [handlePushNotifications](#handlepushnotifications) - [getAttributionId](#getattributionid) - [In-App Purchase Validation](#in-app-purchase-validation) - [validateAndSendInAppPurchase (V2 - Recommended)](#validateandsendinapppurchase-v2---recommended) - [validateAndSendInAppPurchase (Legacy - Deprecated)](#validateandsendinapppurchase-legacy---deprecated) - [setCollectOaid](#setcollectoaid) - [setDisableAdvertisingIdentifiers](#setdisableadvertisingidentifiers) - [setDisableNetworkData](#setdisablenetworkdata) - [iOS Only API](#ios-only-api) - [setDisableCollectAppleAdSupport](#setdisablecollectappleadsupport) - [handlePushNotifications(iOS)](#handlepushnotificationsios) - [setShouldCollectDeviceName](#setshouldcollectdevicename) - [setDisableCollectIAd](#setdisablecollectiad) - [setUseReceiptValidationSandbox](#setusereceiptvalidationsandbox) - [setUseUninstallSandbox](#setuseuninstallsandbox) - [In-App Purchase Validation (iOS)](#in-app-purchase-validation-ios) - [validateAndSendInAppPurchase (V2 - Recommended) {#validateandsendinapppurchase-v2---recommended-1}](#validateandsendinapppurchase-v2---recommended-validateandsendinapppurchase-v2---recommended-1) - [validateAndSendInAppPurchase (Legacy - Deprecated) {#validateandsendinapppurchase-legacy-1}](#validateandsendinapppurchase-legacy---deprecated-validateandsendinapppurchase-legacy-1) - [registerUninstall](#registeruninstall) - [handleOpenUrl](#handleopenurl) - [waitForATTUserAuthorizationWithTimeoutInterval](#waitforattuserauthorizationwithtimeoutinterval) - [disableSKAdNetwork](#disableskadnetwork) - [setLanguage](#setlanguage) - [disableIDFVCollection](#disableidfvcollection) - [IAppsFlyerConversionData](#iappsflyerconversiondata) - [onConversionDataSuccess](#onconversiondatasuccess) - [onConversionDataFail](#onconversiondatafail) - [onAppOpenAttribution](#onappopenattribution) - [onAppOpenAttributionFailure](#onappopenattributionfailure) - [IAppsFlyerUserInvite](#iappsflyeruserinvite) - [onInviteLinkGenerated](#oninvitelinkgenerated) - [onInviteLinkGeneratedFailure](#oninvitelinkgeneratedfailure) - [onOpenStoreLinkGenerated](#onopenstorelinkgenerated) - [IAppsFlyerValidateReceipt](#iappsflyervalidatereceipt) - [didFinishValidateReceipt](#didfinishvalidatereceipt) - [IAppsFlyerValidateAndLog](#iappsflyervalidateandlog) - [onValidateAndLogComplete](#onvalidateandlogcomplete) - [onValidateAndLogFailure](#onvalidateandlogfailure) - [Events](#events) - [onRequestResponse](#onrequestresponse) - [onInAppResponse](#oninappresponse) - [onDeepLinkReceived](#ondeeplinkreceived) --- ## Android, iOS and Windows API ### initSDK **`void initSDK(string devKey, string appID, MonoBehaviour gameObject)`** Initialize the AppsFlyer SDK with the devKey and appID. The dev key is required for all apps and the appID is required only for iOS. If you app is for Android only pass null for the appID. | parameter | type | description | | ----------- |-----------------------------|--------------| | `dev_key` | `string` | AppsFlyer's Dev-Key, which is accessible from your AppsFlyer account under 'App Settings' in the dashboard. | | `app_id` | `string` | Your app's Apple ID. | | `gameObject` (optional) | `MonoBehaviour` | The game object containing the IAppsFlyerConversionData interface. | *Example:* ```c# AppsFlyer.initSDK("dev_key", "app_id"); // without deeplinking AppsFlyer.initSDK("dev_key", "app_id", this); // with deeplinking ``` **Note :** You only need to implement the SDK **with deeplinking** if you are using the `IAppsFlyerConversionData` interface. --- ### startSDK **`void startSDK()`** Once this API is invoked the SDK will start, sessions will be immediately sent, and all background foreground transitions will record a session. *Example:* ```c# AppsFlyer.startSDK(); ``` --- ## Android and iOS API ### stopSDK **`void stopSDK(bool isSDKStopped)`** In some extreme cases you might want to shut down all SDK functions due to legal and privacy compliance. This can be achieved with the stopSDK API. Once this API is invoked, our SDK no longer communicates with our servers and stops functioning. There are several different scenarios for user opt-out. We highly recommend following the exact instructions for the scenario, that is relevant for your app. In any event, the SDK can be reactivated by calling the same API, by passing false. **Important :** Do not call startSDK() if stopSDK() is set to true. To restart SDK functions again, use the following API: `AppsFlyer.stopSDK(false);` **Warning** Use the stopSDK API only in cases where you want to fully ignore the user's SDK functions. Using this API SEVERELY impacts your attribution, data collection and deep linking mechanism. | parameter | type | description | | ------------- |---------|---------------------------- | | `isSDKStopped` | `bool` | True if the SDK is stopped (default value is false). | *Example:* ```c# AppsFlyer.stopSDK(true); ``` --- ### isSDKStopped **`bool isSDKStopped()`** Was the stopSDK(boolean) API set to `true`. *Example:* ```c# if (!AppsFlyer.isSDKStopped()) { } ``` --- ### getSdkVersion **`string getSdkVersion()`** Get the AppsFlyer SDK version used in the app. *Example:* ```c# string version = AppsFlyer.getSdkVersion(); ``` --- ### setIsDebug **`void setIsDebug(bool shouldEnable)`** Enables Debug logs for the AppsFlyer SDK. **Warning** Only set to true in development / debug. | parameter | type | description | | ----------- |------- |---------------------------------------------| | `shouldEnable` | `bool` | True if debug mode is on (default is false) | *Example:* ```c# AppsFlyer.setIsDebug(true); ``` --- ### addPushNotificationDeepLinkPath **`void addPushNotificationDeepLinkPath(params string[] paths)`** Adds array of keys, which are used to compose key path to resolve deeplink from push notification payload. | parameter | type | description | | ----------- |------- |---------------------------------------------| | `paths` | `string[]` | array of strings that represent the key path to the deeplink in the push notification payload | *Example:* **Usage example** Basic configuration: ```c# AppsFlyer.addPushNotificationDeepLinkPath("af_push_link") ``` This call matches the following payload structure: ```json { "af_push_link": "https://yourdeeplink2.onelink.me" } ``` ֿAdvanced configuration: ```c# string[] paths = {"deeply", "nested", "deep_link"}; AppsFlyer.addPushNotificationDeepLinkPath(paths); ``` This call matches the following payload structure: ```json { "deeply": { "nested": { "deep_link": "https://yourdeeplink2.onelink.me" } } } ``` --- ### setCustomerUserId **`void setCustomerUserId(string id)`** Setting your own Custom ID enables you to cross-reference your own unique ID with AppsFlyer’s user ID and the other devices’ IDs. This ID is available in AppsFlyer CSV reports along with postbacks APIs for cross-referencing with your internal IDs. | parameter | type | description | | --------- |--------- |-------------- | | `id` | `string` | custom user ID | *Example:* ```c# AppsFlyer.setCustomerUserId("custom_user_id"); ``` --- ### setAppInviteOneLinkID **`void setAppInviteOneLinkID(string oneLinkId)`** Set the OneLink ID that should be used for User-Invite-API The link that is generated for the user invite will use this OneLink as the base link. | parameter | type | description | | ----------- |--------- |----------------------------------------| | `oneLinkId` | `string` | OneLink ID for User-Invite attribution | *Example:* ```c# AppsFlyer.setAppInviteOneLinkID("abcd"); ``` --- ### setAdditionalData **`void setAdditionalData(Dictionary customData)`** The setAdditionalData API is required to integrate on the SDK level with several external partner platforms, including Segment, Adobe and Urban Airship. Use this API only if the integration article of the platform specifically states setAdditionalData API is needed. | parameter | type | description | | ----------- |----------------------------- |-------------- | | `customData` | `Dictionary` | additional data | *Example:* ```c# Dictionary customData = new Dictionary(); customData.Add("custom1", "someData"); AppsFlyer.setAdditionalData(customData); ``` --- ### setResolveDeepLinkURLs **`void setResolveDeepLinkURLs(params string[] urls)`** If you are using OneLinks which support Android App Links and wrapping them with a 3rd Party Universal Link, you can use the setResolveDeepLinkURLs API to notify the AppsFlyer SDK which click domains that invoke the app should be resolved by the SDK and have the underlying OneLink extracted from them. This will allow you to maintain deep linking and attribution while wrapping the OneLink with a 3rd party Universal Link. Make sure to call this API before SDK initialization. | parameter | type | description | | ----------|-------------------|-------------- | | `urls` | `params string[]` | array of urls | *Example:* ```c# AppsFlyer.setResolveDeepLinkURLs("test.com", "test2.ca"); ``` --- ### setOneLinkCustomDomain **`void setOneLinkCustomDomain(params string[] domains)`** Advertisers can use this method to set vanity onelink domains. | parameter | type | description | | ---------- | ----------------- |-------------------------| | `domains` | `params string[]` | array of custom domains | *Example:* ```c# AppsFlyer.setOneLinkCustomDomain("test.domain", "test2.domain"); ``` --- ### setCurrencyCode **`void setCurrencyCode(string currencyCode)`** Setting user local currency code for in-app purchases. The currency code should be a 3 character ISO 4217 code. (default is USD). You can set the currency code for all events by calling the following method. | parameter | type | description | | ----------- |----------|---------------------------------------------| | `currencyCode` | `string` | 3 character ISO 4217 code. (default is USD) | *Example:* ```c# AppsFlyer.setCurrencyCode("GBP"); ``` --- --- ### setDeepLinkTimeout **`void setDeepLinkTimeout(long deepLinkTimeout)`** Setting the deepLink timeout value that should be used for DDL. If you want to use it, set it before the DDL setting. | parameter | type | description | | ------------------ |----------|------------------| | `deepLinkTimeout` | `long` | in milliseconds | *Example:* ```c# AppsFlyer.setDeepLinkTimeout(2000); ``` --- ### enableTCFDataCollection **`void enableTCFDataCollection(bool shouldCollectTcfData)`** Calling enableTCFDataCollection(true) will enable collecting and sending any TCF related data. Calling enableTCFDataCollection(false) will disable the collection of TCF related data and from sending it. | parameter | type | description | | ------------------------ |----------|-------------------------------- | | `shouldCollectTcfData` | `bool` | true to enable data collection | *Example:* ```c# AppsFlyer.enableTCFDataCollection(true);; ``` --- ### setConsentData **`void setConsentData(AppsFlyerConsent appsFlyerConsent)`** Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage purposes within the application. | parameter | type | description | | --------------------------- |---------- |------------------------------------ | | `appsFlyerConsent` | `AppsFlyerConsent` | Instance of AppsFlyerConsent class | *Example:* ```c# AppsFlyerConsent consent = new AppsFlyerConsent(true, false, false, false); AppsFlyer.setConsentData(consent); ``` --- ### recordLocation **`void recordLocation(double latitude, double longitude)`** Manually record the location of the user. | parameter | type | description | | ----------- |--------- |-------------- | | `latitude` | `double` | latitude of user | | `longitude` | `double` | longitude of user | *Example:* ```c# AppsFlyer.recordLocation(40.7128, 74.0060); ``` --- ### anonymizeUser **`void anonymizeUser(bool shouldAnonymizeUser)`** AppsFlyer provides you with a method to anonymize specific user identifiers in AppsFlyer analytics. This method complies with the latest privacy requirements and complies with Facebook data and privacy policies. Default is NO, meaning no anonymization is performed by default. Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions. You can cancel anonymization by calling anonymizeUser again, set to false. **Warning** Anonymizing users SEVERELY impacts your attribution information. Use this option ONLY for regions which legally prevents you from collecting your users' information. | parameter | type | description | | -------------------- |--------|-------------------------------| | `shouldAnonymizeUser` | `bool` | true to perform anonymization | *Example:* ```c# AppsFlyer.anonymizeUser(true); ``` --- ### getAppsFlyerId **`string getAppsFlyerId()`** AppsFlyer's unique device ID is created for every new install of an app. Use the following API to obtain AppsFlyer’s Unique ID. *Example:* ```c# string uid = AppsFlyer.getAppsFlyerId(); ``` --- ### setMinTimeBetweenSessions **`void setMinTimeBetweenSessions(int seconds)`** By default, at least 5 seconds must lapse between 2 app launches to count as separate 2 sessions (more about counting sessions). However, you can use the following API to set your custom value for the minimum required time between sessions. **Note:** Setting a high value to the custom time between launches may badly impact APIs relying on sessions data, such as deep linking. | parameter | type | description | | ----------- |------- |--------------------------------------------- | | `seconds` | `int` | time between sessions (default is 5 seconds) | *Example:* ```c# AppsFlyer.setMinTimeBetweenSessions(4); ``` --- ### setHost **`void setHost(string hostPrefixName, string hostName)`** Set a custom host. | parameter | type | description | | ----------- |---------- |--------------| | `hostPrefixName` | `string` | | | `hostName` | `string` | | *Example:* ```c# AppsFlyer.setHost("hostPrefixName","hostName"); ``` --- ### setUserEmails *Soon to be deprecated* **`void setUserEmails(EmailCryptType cryptMethod, params string[] emails)`** Set the user emails and encrypt them. cryptMethod Encryption methods: EmailCryptType.EmailCryptTypeSHA256 EmailCryptType.EmailCryptTypeNone | parameter | type | description | | ----------- |-------------------|---------------------------| | `cryptMethod` | `EmailCryptType` | none, or sha256 | | `emails` | `params string[]` | list of emails | *Example:* ```c# AppsFlyer.setUserEmails(EmailCryptType.EmailCryptTypeSHA256, "test1@test1.com", "test2@test2.com"); ``` --- ### setPhoneNumber *Soon to be deprecated* **`void setPhoneNumber(string phoneNumber)`** Set the user phone number. | parameter | type | description | | ----------- |-------------------|---------------------------| | `phoneNumber` | `string` | | *Example:* ```c# AppsFlyer.setPhoneNumber("4166358181"); ``` --- ### getConversionData **`void getConversionData(string objectName);`** Register a Conversion Data Listener. Allows the developer to access the user attribution data in real-time for every new install, directly from the SDK level. By doing this you can serve users with personalized content or send them to specific activities within the app, which can greatly enhance their engagement with your app. Get the callbacks by implementing the IAppsFlyerConversionData interface. | parameter | type | description | | ----------- |----------|-------------------------------------------------------- | | `objectName` | `string` | game object with the IAppsFlyerConversionData interface | *Example:* ```c# AppsFlyer.getConversionData(gameObject.name); ``` --- ### attributeAndOpenStore **`void attributeAndOpenStore(string appID, string campaign, Dictionary userParams, MonoBehaviour gameObject)`** Use the following API to attribute the click and launch the app store's app page. Get the callbacks by implementing the IAppsFlyerUserInvite interface. | parameter | type | description | | ----------- |----------------------------- |-----------------------------------------------------| | `appID` | `string` | | | `campaign` | `string` | | | `userParams` | `Dictionary `| | | `gameObject` | `MonoBehaviour` | game object with the IAppsFlyerUserInvite interface | *Example:* ```c# Dictionary parameters = new Dictionary(); parameters.Add("af_sub1", "val"); parameters.Add("custom_param", "val2"); AppsFlyer.attributeAndOpenStore("123456789", "test campaign", parameters, this); ``` --- ### logAdRevenue **`void logAdRevenue(AFAdRevenueData adRevenueData, Dictionary additionalParameters)`** Logs ad revenue data along with additional parameters if provided. | parameter | type | description | | ------------------- |----------------------------- |-----------------------------------------------------| | `adRevenueData` | `AFAdRevenueData` | Instance of AFAdRevenueData containing ad revenue information | | `additionalParameters` | `Dictionary `| An optional map of additional parameters to be logged with ad revenue data | *Example:* ```c# Dictionary parameters = new Dictionary(); parameters.Add("value1", "5"); parameters.Add(AdRevenueScheme.COUNTRY, "USA"); var logRevenue = new AFAdRevenueData("monetizationNetworkEx", MediationNetwork.GoogleAdMob, "USD", 0.99); AppsFlyer.logAdRevenue(logRevenue, parameters); ``` --- ### recordCrossPromoteImpression **`void recordCrossPromoteImpression(string appID, string campaign);`** To attribute an impression use the following API call. Make sure to use the promoted App ID as it appears within the AppsFlyer dashboard. | parameter | type | description | | ----------- |-----------|--------------| | `appID` | `string` | appID | | `campaign` | `string` | campaign | | `params` | `Dictionary` | additional params | *Example:* ```c# Dictionary parameters = new Dictionary(); parameters.Add("af_sub1", "val"); parameters.Add("custom_param", "val2"); AppsFlyer.recordCrossPromoteImpression("appID", "campaign", parameters); ``` --- ### generateUserInviteLink **`void generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject)`** The LinkGenerator class builds the invite URL according to various setter methods which allow passing on additional information on the click. See - https://support.appsflyer.com/hc/en-us/articles/115004480866-User-invite-attribution- | parameter | type | description | | ----------- |----------------------------- |-----------------------------------------------------| | `parameters` | `Dictionary` | | | `gameObject` | `MonoBehaviour` | game object with the IAppsFlyerUserInvite interface | *Example:* ```c# AppsFlyer.generateUserInviteLink(params, this); ``` --- ### setSharingFilterForAllPartners *Deprecated* **`void setSharingFilterForAllPartners()`** Used by advertisers to exclude all networks/integrated partners from getting data. *Example:* ```c# AppsFlyer.setSharingFilterForAllPartners(); ``` --- ### setSharingFilter *Deprecated* **`void setSharingFilter(params string[] partners)`** Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data. | parameter | type | description | | ----------- |----------------------------- |-----------------------------------------------------| | `partners` | `params string[] partners` | partners to exclude from getting data | *Example:* ```c# AppsFlyer.setSharingFilter("googleadwords_int","snapchat_int","doubleclick_int"); ``` --- ### setSharingFilterForPartners **`void setSharingFilterForPartners(params string[] partners)`** Used by advertisers to set some (one or more) networks/integrated partners to exclude from getting data. | parameter | type | description | | ----------- |----------------------------- |-----------------------------------------------------| | `partners` | `params string[] partners` | partners to exclude from getting data | *Example:* ```c# AppsFlyer.setSharingFilterForPartners("partner1_int"); // Single partner AppsFlyer.setSharingFilterForPartners("partner1_int", "partner2_int"); // Multiple partners AppsFlyer.setSharingFilterForPartners("all"); // All partners AppsFlyer.setSharingFilterForPartners(""); // Reset list (default) AppsFlyer.setSharingFilterForPartners(); // Reset list (default) ``` --- ### setPartnerData **`void setPartnerData(string partnerID, params string[] partnerInfo)`** Allows sending custom data for partner integration purposes. | parameter | type |description | | ----------- |------------------------------| -----------------------------------------| | `partnerID` | `string` | ID of the partner (usually suffixed with "_int").| |`partnerInfo` | `params string[]` | Customer data, depends on the integration configuration with the specific partner. | *Example:* ```c# Dictionary partnerInfo = new Dictionary(); partnerInfo.Add("puid", "1234567890"); AppsFlyer.setPartnerData("partner_test", partnerInfo); ``` ---- ## Android Only API ### updateServerUninstallToken **`void updateServerUninstallToken(string token)`** Manually pass the Firebase Device Token for Uninstall measurement. | parameter | type | description | | ---------- |---------- |--------------------| | `token` | `string` | Firebase FCM token | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.updateServerUninstallToken("token"); #endif ``` --- ### setImeiData **`void setImeiData(string imei)`** By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat (4.4) and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS). Use this API to explicitly send IMEI to AppsFlyer. | parameter | type | description | | ----------- |----------|-------------- | | `imei` | `string` | device's IMEI | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setImeiData("imei"); #endif ``` --- ### setAndroidIdData **`void setAndroidIdData(string androidId)`** By default, IMEI and Android ID are not collected by the SDK if the OS version is higher than KitKat(4.4)and the device contains Google Play Services(on SDK versions 4.8.8 and below the specific app needed GPS). Use this API to explicitly send Android ID to AppsFlyer. | parameter | type | description | | ----------- | ---------|--------------------- | | `androidId` | `string` | device's Android ID | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setAndroidIdData("androidId"); #endif ``` --- ### waitForCustomerUserId **`void waitForCustomerUserId(bool wait)`** It is possible to delay the SDK Initialization until the customerUserID is set. This feature makes sure that the SDK doesn't begin functioning until the customerUserID is provided. If this API is used, all in-app events and any other SDK API calls are discarded, until the customerUserID is provided. | parameter | type | description | | ---------- |-------- |-----------------------------------------------------| | `wait` | `bool` | True if you want the SDK to wait for customerUserID | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.waitForCustomerUserId(true); #endif ``` --- ### setCustomerIdAndStartSDK **`void setCustomerIdAndStartSDK(string id)`** > ⚠️ Before calling this method, the method [`waitForCustomerUserId`](#waitforcustomeruserid) must be called Use this API to provide the SDK with the relevant customer user id and trigger the SDK to begin its normal activity. | parameter | type | description | | ----------- |---------- |------------------------ | | `id` | `string` | Customer ID for client. | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCustomerIdStartSDK("id"); #endif ``` --- ### getOutOfStore **`string getOutOfStore()`** Get the current AF_STORE value. *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR string af_store = AppsFlyer.getOutOfStore(); #endif ``` --- ### setOutOfStore **`void setOutOfStore(string sourceName)`** Manually set the AF_STORE value. | parameter | type | description | | ----------- |---------- |--------------| | `sourceName` | `string` | | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setOutOfStore("sourceName"); #endif ``` --- ### setCollectAndroidID **`void setCollectAndroidID(bool isCollect)`** Opt-out of collection of Android ID. If the app does NOT contain Google Play Services, Android ID is collected by the SDK. However, apps with Google play services should avoid Android ID collection as this is in violation of the Google Play policy. | parameter | type | description | | ----------- |--------- |--------------| | `isCollect` | `bool` | | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCollectAndroidID(true); #endif ``` --- ### setCollectIMEI **`void setCollectIMEI(bool isCollect)`** Opt-out of collection of IMEI. If the app does NOT contain Google Play Services, device IMEI is collected by the SDK. However, apps with Google play services should avoid IMEI collection as this is in violation of the Google Play policy. | parameter | type | description | | ----------- |-----------------------------|--------------| | `isCollect` | `bool` | | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCollectIMEI(true); #endif ``` --- ### setIsUpdate **`void setIsUpdate(bool isUpdate)`** Manually set that the application was updated. | parameter | type | description | | ----------- |-------- |-------------------------| | `isUpdate` | `bool` | true if app was updated | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setIsUpdate(true); #endif ``` --- ### setPreinstallAttribution **`void setPreinstallAttribution(string mediaSource, string campaign, string siteId)`** Specify the manufacturer or media source name to which the preinstall is attributed. | parameter | type | description | | ----------- |----------|---------------------------------------------------------------| | `mediaSource` | `string` | Manufacturer or media source name for preinstall attribution. | | `campaign` | `string` | Campaign name for preinstall attribution. | | `siteId` | `string` | Site ID for preinstall attribution. | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setPreinstallAttribution("mediaSource", "campaign", "siteId"); #endif ``` --- ### isPreInstalledApp **`bool isPreInstalledApp()`** Boolean indicator for preinstall by Manufacturer. *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR if (AppsFlyer.isPreInstalledApp()) { } #endif ``` --- ### handlePushNotifications **`void handlePushNotifications()`** When the handlePushNotifications API is called push notifications will be recorded. *Example:* ```c# AppsFlyer.handlePushNotifications(); ``` --- ### getAttributionId **`string getAttributionId()`** Get the Facebook attribution ID, if one exists. *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR string attributionId = AppsFlyer.getAttributionId(); #endif ``` --- ## In-App Purchase Validation AppsFlyer provides two versions of the `validateAndSendInAppPurchase` method: - **V2 (Recommended)**: Uses structured data classes (`AFPurchaseDetailsAndroid`/`AFSDKPurchaseDetailsIOS`) for better type safety and cleaner code - **Legacy**: Uses individual string parameters (deprecated, maintained for backward compatibility) ### validateAndSendInAppPurchase (V2 - Recommended) **`void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject)`** API for server verification of in-app purchases (V2). An af_purchase event with the relevant values will be automatically sent if the validation is successful. | parameter | type | description | | ----------- |------------------------------|----------------------------------------------------| | `details` | `AFPurchaseDetailsAndroid` | Instance of AFPurchaseDetailsAndroid class | | `purchaseAdditionalDetails` | `Dictionary` | Additional parameters to be sent with the purchase | | `gameObject` | `MonoBehaviour` | Game object for the callbacks to be sent | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AFPurchaseDetailsAndroid details = new AFPurchaseDetailsAndroid(AFPurchaseType.Subscription, "token", "productId"); AppsFlyer.validateAndSendInAppPurchase( details, null, this); #endif ``` --- ### validateAndSendInAppPurchase (Legacy - Deprecated) **`void validateAndSendInAppPurchase(string publicKey, string signature, string purchaseData, string price, string currency, Dictionary additionalParameters, MonoBehaviour gameObject)`** **⚠️ Deprecated:** This legacy API is deprecated. Use the V2 version with `AFPurchaseDetailsAndroid` instead. This method is maintained for backward compatibility only. API for server verification of in-app purchases. An af_purchase event with the relevant values will be automatically sent if the validation is successful. | parameter | type | description | | ----------- |----------|--------------| | `publicKey` | `string` | License Key obtained from the Google Play Console. | | `signature` | `string` | data.INAPP_DATA_SIGNATURE. | | `purchaseData` | `string` | data.INAPP_PURCHASE_DATA | | `price` | `string` | Purchase price | | `currency` | `string` | Purchase currency | |`additionalParameters` | `Dictionary` | Parameters to be sent with the purchase | | `gameObject` | `MonoBehaviour` | Game object for the callbacks to be sent | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.validateAndSendInAppPurchase( "publicKey", "signature", "purchaseData", "price", "currency", null, this); #endif ``` --- ### setCollectOaid **`void setCollectOaid(boolean isCollect)`** setCollectOaid You must include the appsflyer oaid library for this api to work. | parameter | type | description | | ----------- |-------- |-------------------------| | `isCollect` | `bool` | true to allow oaid collection | *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCollectOaid(true); #endif ``` --- ### setDisableAdvertisingIdentifiers **`void setDisableAdvertisingIdentifiers(boolean disable)`** setDisableAdvertisingIdentifiers Disables collection of various Advertising IDs by the SDK. This includes Google Advertising ID (GAID), OAID and Amazon Advertising ID (AAID) | parameter | type | description | | ----------- |-------- |-------------------------| | `disable` | `bool` | true to disable| *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setDisableAdvertisingIdentifiers(true); #endif ``` --- --- ### setDisableNetworkData **`void setDisableNetworkData(boolean disable)`** setDisableNetworkData Use to opt-out of collecting the network operator name (carrier) and sim operator name from the device. | parameter | type | description | | ----------- |-------- |-------------------------| | `disable` | `bool` | true to opt-out| *Example:* ```c# #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setDisableNetworkData(true); #endif ``` --- ## iOS Only API ### setDisableCollectAppleAdSupport **`void setDisableCollectAppleAdSupport(bool disable)`** AppsFlyer SDK collects Apple's `advertisingIdentifier` if the `AdSupport.framework` is included in the SDK. You can disable this behavior by setting the following property to true. | parameter | type | description | | ----------- |----------|--------------| | `disable` | `bool` | | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setDisableCollectAppleAdSupport(true); #endif ``` --- ### handlePushNotifications(iOS) **`void handlePushNotification(Dictionary pushPayload)`** When the handlePushNotifications API is called from a service that is swizzling, like Firebase, the push notifications payload will be handled by the AppsflyerSDK. | parameter | type | description | | --------------|------------------------------|-------------------------------| | `pushPayload` | `Dictionary` | the push notification payload | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR // e.Message.Data = push notification payload var dataDict = new Dictionary(e.Message.Data); AppsFlyeriOS.handlePushNotification(dataDict); #endif ``` --- ### setShouldCollectDeviceName **`void setShouldCollectDeviceName(bool shouldCollectDeviceName)`** Set this flag to true, to collect the current device name(e.g. "My iPhone"). Default value is false. | parameter | type | description | | ---------------------- |---------- |--------------| | `shouldCollectDeviceName` | `bool` | | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setShouldCollectDeviceName(true); #endif ``` --- ### setDisableCollectIAd **`void setDisableCollectIAd(bool disableCollectIAd)`** Opt-out for Apple Search Ads attributions. | parameter | type | description | | ----------- |----------|--------------| | `disableCollectIAd` | `bool` | | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setDisableCollectIAd(true); #endif ``` --- ### setUseReceiptValidationSandbox **`void setUseReceiptValidationSandbox(bool useReceiptValidationSandbox)`** In app purchase receipt validation Apple environment(production or sandbox). The default value is false. | parameter | type | description | | ---------------------------- |---------- |--------------------------------------------- | | `useReceiptValidationSandbox` | `bool` | true if In app purchase is done with sandbox | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setUseReceiptValidationSandbox(true); #endif ``` --- ### setUseUninstallSandbox **`void setUseUninstallSandbox(bool useUninstallSandbox)`** Set this flag to test uninstall on Apple environment(production or sandbox). The default value is false. | parameter | type | description | | ----------- |------- |---------------------------------------- | | `useUninstallSandbox` | `bool` | true if you are using a APN certificate | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setUseUninstallSandbox(true); #endif ``` --- ## In-App Purchase Validation (iOS) AppsFlyer provides two versions of the `validateAndSendInAppPurchase` method for iOS: - **V2 (Recommended)**: Uses structured data classes (`AFSDKPurchaseDetailsIOS`) for better type safety and cleaner code - **Legacy**: Uses individual string parameters (deprecated, maintained for backward compatibility) ### validateAndSendInAppPurchase (V2 - Recommended) {#validateandsendinapppurchase-v2---recommended-1} **`void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary purchaseAdditionalDetails, MonoBehaviour gameObject)`** To send and validate in app purchases you can call this method from the processPurchase method (V2). > **Note on iOS Error Handling:** The `onValidateAndLogFailure` callback is only triggered for actual errors (network errors, invalid parameters, etc.). Validation failures (e.g., invalid receipt) are returned through the `onValidateAndLogComplete` callback in the response dictionary. Always check the response to determine if validation was successful. | parameter | type | description | | ----------- |------------------- | ------------------------------------------| | `details` | `AFSDKPurchaseDetailsIOS` | Instance of AFSDKPurchaseDetailsIOS class | | `purchaseAdditionalDetails` | `Dictionary` | Additional parameters to be sent with the purchase | | `gameObject` | `MonoBehaviour` | Game object for the callbacks to be sent | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AFSDKPurchaseDetailsIOS details = AFSDKPurchaseDetailsIOS.Init("productId", "price", "currency", "transactionId"); AppsFlyer.validateAndSendInAppPurchase( details, null, this); #endif ``` --- ### validateAndSendInAppPurchase (Legacy - Deprecated) {#validateandsendinapppurchase-legacy-1} **`void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string transactionId, Dictionary additionalParameters, MonoBehaviour gameObject)`** **⚠️ Deprecated:** This legacy API is deprecated. Use the V2 version with `AFSDKPurchaseDetailsIOS` instead. This method is maintained for backward compatibility only. To send and validate in app purchases you can call this method from the processPurchase method. | parameter | type | description | | ----------- |------------------- | --------------| | `productIdentifier` | `string` | The product identifier | | `price` | `string` | The product price | | `currency` | `string` | The product currency | | `transactionId` | `string` | The purchase transaction ID | | `additionalParameters` | `Dictionary` | Additional parameters to be sent with the purchase | | `gameObject` | `MonoBehaviour` | Game object for the callbacks | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.validateAndSendInAppPurchase( "productIdentifier", "price", "currency", "transactionId", null, this); #endif ``` --- ### registerUninstall **` void registerUninstall(byte[] deviceToken)`** Register uninstall - you should register for remote notification and provide AppsFlyer the push device token. | parameter | type | description | | ----------- |---------- |--------------| | `deviceToken` | `byte[]` | APN token | *Example:* ```c# private bool tokenSent; void Update() { #if UNITY_IOS && !UNITY_EDITOR if (!tokenSent) { byte[] token = UnityEngine.iOS.NotificationServices.deviceToken; if (token != null) { AppsFlyer.registerUninstall(token); tokenSent = true; } } #endif } ``` --- ### handleOpenUrl **`void handleOpenUrl(string url, string sourceApplication, string annotation)`** In case you want to track deep linking manually call handleOpenUrl. The continueUserActivity and onOpenURL are implemented in the AppsFlyerAppController.mm class, so only use this method if the other methods do not cover your apps deeplinking needs. | parameter | type | description | | ----------- |---------- |--------------| | `url` | `string` | The URL to be passed to your AppDelegate | | `sourceApplication` | `string` | The sourceApplication to be passed to your AppDelegate | | `annotation` | `string` | The annotation to be passed to your app delegate | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.handleOpenUrl(string url, string sourceApplication, string annotation); #endif ``` --- ### waitForATTUserAuthorizationWithTimeoutInterval **` void waitForATTUserAuthorizationWithTimeoutInterval(int timeoutInterval)`** See [here](https://support.appsflyer.com/hc/en-us/articles/207032066-iOS-SDK-V6-X-integration-guide-for-developers#integration-33-configuring-app-tracking-transparency-att-support) for more info. | parameter | type | description | | ----------- |---------- |--------------| | `timeoutInterval` | `int` | Time to wait for idfa | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` --- ### disableSKAdNetwork **` bools disableSKAdNetwork(bool isDisabled)`** | parameter | type | description | | ----------- |---------- |--------------| | `isDisabled` | `bool` | True to disable SKAdNetwork | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.disableSKAdNetwork(true); #endif ``` --- ### setLanguage **` setCurrentDeviceLanguage(string language)`** | parameter | type | description | | ----------- |---------- |--------------| | `language` | `String` | The language to set | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setCurrentDeviceLanguage("english"); #endif ``` --- ### disableIDFVCollection **` disableIDFVCollection(bool isDisabled)`** | parameter | type | description | | ----------- |---------- |--------------| | `isDisabled` | `bool` | True to disable IDFV collection | *Example:* ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.disableIDFVCollection(true); #endif ``` --- ## IAppsFlyerConversionData ### onConversionDataSuccess **`public void onConversionDataSuccess(string conversionData)`** ConversionData contains information about install.
Organic/non-organic, etc. See [here](https://support.appsflyer.com/hc/en-us/articles/360000726098-Conversion-Data-Scenarios#Introduction) for more info. | parameter | type | description | | ---------- |---------- |--------------------| | `conversionData` | `string` | JSON string of the returned conversion data | *Example:* ```c# public void onConversionDataSuccess(string conversionData) { AppsFlyer.AFLog("onConversionDataSuccess", conversionData); Dictionary conversionDataDictionary = AppsFlyer.CallbackStringToDictionary(conversionData); // add deferred deeplink logic here } ``` --- ### onConversionDataFail **`public void onConversionDataFail(string error)`** | parameter | type | description | | ---------- |---------- |--------------------| | `error` | `string` | A string describing the error | *Example:* ```c# public void onConversionDataFail(string error) { AppsFlyer.AFLog("onConversionDataFail", error); } ``` --- ### onAppOpenAttribution **`public void onAppOpenAttribution(string attributionData)`** attributionData contains information about OneLink, deeplink. | parameter | type | description | | ---------- |---------- |--------------------| | `attributionData` | `string` | JSON string of the returned deeplink data | *Example:* ```c# public void onAppOpenAttribution(string attributionData) { AppsFlyer.AFLog("onAppOpenAttribution", attributionData); Dictionary attributionDataDictionary = AppsFlyer.CallbackStringToDictionary(attributionData); // add direct deeplink logic here } ``` --- ### onAppOpenAttributionFailure **`public void onAppOpenAttributionFailure(string error)`** Any errors that occurred during the attribution request. | parameter | type | description | | ---------- |---------- |--------------------| | `error` | `string` | string describing the error | *Example:* ```c# public void onAppOpenAttributionFailure(string error) { AppsFlyer.AFLog("onAppOpenAttributionFailure", error); } ``` --- ## IAppsFlyerUserInvite ### onInviteLinkGenerated **`public void onInviteLinkGenerated(string link)`** The success callback for generating OneLink URLs. | parameter | type | description | | ---------- |---------- |--------------------| | `link` | `string` | generated link | *Example:* ```c# public void onInviteLinkGenerated(string link) { } ``` --- ### onInviteLinkGeneratedFailure **`public void onInviteLinkGeneratedFailure(string error)`** The error callback for generating OneLink URLs | parameter | type | description | | ---------- |---------- |--------------------| | `error` | `string` | A string describing the error | *Example:* ```c# public void onInviteLinkGeneratedFailure(string error) { AppsFlyer.AFLog("onInviteLinkGeneratedFailure", error); } ``` --- ### onOpenStoreLinkGenerated **`public void onOpenStoreLinkGenerated(string link)`** (ios only) iOS allows you to utilize the StoreKit component to open the App Store while remaining in the context of your app.
More details at [here](https://support.appsflyer.com/hc/en-us/articles/115004481946-Cross-Promotion-Tracking#tracking-cross-promotion-impressions) | parameter | type | description | | ---------- |---------- |--------------------| | `attributionData` | `string` | JSON string of the returned deeplink data | *Example:* ```c# public void onOpenStoreLinkGenerated(string link) { } ``` --- ## IAppsFlyerValidateReceipt ### didFinishValidateReceipt **`public void didFinishValidateReceipt(string result)`** The success callback for validateAndSendInAppPurchase API.
For Android : the callback will return "Validate success".
For iOS : the callback will return a JSON string from apples verifyReceipt API.
| parameter | type | description | | ---------- |---------- |--------------------| | `result` | `string` | validate result | *Example:* ```c# public void didFinishValidateReceipt(string link) { } ``` --- ## IAppsFlyerValidateAndLog ### onValidateAndLogComplete **`public void onValidateAndLogComplete(string result)`** The success callback for validateAndSendInAppPurchase API.
The callback will return a JSON string which can be converted to dictionary.
> **Note for iOS:** On iOS, this callback is invoked for all successful API calls, including cases where validation fails (e.g., invalid receipt). Always check the response dictionary to determine the actual validation result. Only network errors and invalid parameters trigger `onValidateAndLogFailure`. | parameter | type | description | | ---------- |---------- |--------------------| | `result` | `string` | validate result | *Example:* ```c# public void onValidateAndLogComplete(string result) { AppsFlyer.AFLog("onValidateAndLogComplete", result); Dictionary validateAndLogDataDictionary = AppsFlyer.CallbackStringToDictionary(result); } ``` --- ### onValidateAndLogFailure **`public void onValidateAndLogFailure(string error)`** The error callback for validating receipts.
The callback will return a JSON string which can be converted to dictionary.
> **Note for iOS:** On iOS, this callback is only triggered for actual errors (network errors, invalid parameters, etc.). Validation failures (e.g., invalid receipt) are returned through `onValidateAndLogComplete` in the response dictionary. | parameter | type | description | | ---------- |---------- |---------------------------- | | `error` | `string` | A string describing the error | *Example:* ```c# public void onValidateAndLogFailure(string error) { AppsFlyer.AFLog("onValidateAndLogFailure", error); } ``` --- ## Events ### onRequestResponse **`public static event EventHandler OnRequestResponse`** The callback for Sessions.
| statusCode | errorDescription | | ----------- | ----------- | | 200 | null | | 10 | "Event timeout. Check 'minTimeBetweenSessions' param" | | 11 | "Skipping event because 'isStopTracking' enabled" | | 40 | Network error: Error description comes from Android | | 41 | "No dev key" | | 50 | "Status code failure" + actual response code from the server | *Example:* ```c# AppsFlyer.OnRequestResponse += (sender, args) => { var af_args = args as AppsFlyerRequestEventArgs; AppsFlyer.AFLog("AppsFlyerOnRequestResponse", "status code" + af_args.statusCode); }; ``` --- ### onInAppResponse **`public static event EventHandler OnInAppResponse`** The callback for In-App Events.
| statusCode | errorDescription | | ----------- | ----------- | | 200 | null | | 10 | "Event timeout. Check 'minTimeBetweenSessions' param" | | 11 | "Skipping event because 'isStopTracking' enabled" | | 40 | Network error: Error description comes from Android | | 41 | "No dev key" | | 50 | "Status code failure" + actual response code from the server | *Example:* ```c# AppsFlyer.OnInAppResponse += (sender, args) => { var af_args = args as AppsFlyerRequestEventArgs; AppsFlyer.AFLog("OnRequestResponse", "status code" + af_args.statusCode); }; ``` --- ### onDeepLinkReceived **`public static event EventHandler OnDeepLinkReceived`** The callback for Unified Deeplink API.
*Example:* ```c# // First call init with devKey, appId and gameObject AppsFlyer.initSDK(devKey, appID, this); AppsFlyer.OnDeepLinkReceived += (sender, args) => { var deepLinkEventArgs = args as DeepLinkEventsArgs; // DEEPLINK LOGIC HERE }; ``` --- ================================================ FILE: docs/BasicIntegration.md ================================================ --- title: Integration category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 1 hidden: false --- You can initialize the plugin by using the AppsFlyerObject prefab or manually. - [Using the AppsFlyerObject.prefab](#using-the-appsflyerobjectprefab) - [Manual integration](#manual-integration) - [Collect IDFA with ATTrackingManager](#collect-idfa-with-attrackingmanager) - [Send consent for DMA compliance](#send-consent-for-dma-compliance) - [Sending SKAN postback to Appsflyer](#sending-skan-postback-to-appsflyer) - [MacOS initialization](#macos-initialization) - [Request Listeners (Optional)](#request-listeners-optional) ## Using the AppsFlyerObject.prefab 1. Go to Assets > AppsFlyer and drag AppsFlyerObject.prefab to your scene.
2. Update the following fields: | Setting | Description | | -------- | ------------- | | **Dev Key** | AppsFlyer's [Dev Key](https://support.appsflyer.com/hc/en-us/articles/207032126-Android-SDK-integration-for-developers#integration-31-retrieving-your-dev-key), which is accessible from the AppsFlyer dashboard. | | **App ID** | Your iTunes Application ID. (If your app is not for iOS the leave field empty) | | **Get Conversion Data** | Set this to true if your app is using AppsFlyer for deep linking. | | **Is Debug** | Set this to true to view the debug logs. (for development only!) | 3. Update the code in Assets > AppsFlyer > AppsFlyerObjectScript.cs with other available [API](/docs/api). ## Manual integration Create a game object and add the following init code: ```c# using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour { void Start() { AppsFlyer.initSDK("devkey", "appID"); AppsFlyer.startSDK(); } } ``` > **Note:** > - Make sure not to call destroy on the game object. > - Use [`DontDestroyOnLoad`](https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html) to keep the object when loading a new scene. --- ## Set customer user ID Set your own unique customer user ID (CUID) and cross-reference it with the unique AppsFlyer ID. - Appear in AppsFlyer raw data CSV reports. - Can be used in postback APIs to cross-reference with internal IDs. To set the CUID, use: ```c# AppsFlyer.setCustomerUserId("someId"); ``` **Good practice!** Set the CUID early in the app flow—it is only associated with events reported after its setup. - Recorded events will be associated with the CUID. - Related data will appear in the raw data reports for installs and events.. ### Associate the CUID with the install event If it’s important for you to associate the install event with the CUID, call `setCustomerUserId` before calling `startSDK`. ## Collect IDFA with ATTrackingManager 1. Add the `AppTrackingTransparency` framework to your xcode project. 2. In the `Info.plist`: 1. Add an entry to the list: Press + next to `Information Property List`. 2. Scroll down and select `Privacy - Tracking Usage Description`. 3. Add as the value the wording you want to present to the user when asking for permission to collect the IDFA. 3. Call the `waitForATTUserAuthorizationWithTimeoutInterval` api before `startSDK()` ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` 4. Request the tracking authorization where you wish to display the prompt:
You can use the following [package](https://github.com/Unity-Technologies/com.unity.ads.ios-support) or any other package that allows you to request the tracking authorization. ```c# using Unity.Advertisement.IosSupport; /* ... */ if (ATTrackingStatusBinding.GetAuthorizationTrackingStatus() == ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED) { ATTrackingStatusBinding.RequestAuthorizationTracking(); } /* ... */ ### Customizing the ATT consent dialog The ATT consent dialog can be customized by modifying your Xcode project's `info.plist`: For detailed instructions, see [Apple's documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription). --- ## Send consent for DMA compliance Unity SDK plugin offers two alternative methods for gathering consent data: Through a Consent Management Platform (CMP): If the app uses a CMP that complies with the Transparency and Consent Framework (TCF) v2.2 protocol, the Unity SDK can automatically retrieve the consent details. OR Through a dedicated Unity SDK API: Developers can pass Google's required consent data directly to the Unity SDK using a specific API designed for this purpose. ### Use CMP to collect consent data 1. Initialize the SDK. 2. Call enableTCFDataCollection(true) api before startSDK() to instruct the SDK to collect the TCF data from the device. 3. Use the CMP to decide if you need the consent dialog in the current session to acquire the consent data. If you need the consent dialog move to step 4; otherwise move to step 5. 4. Get confirmation from the CMP that the user has made their consent decision and the data is available. 5. Call start(). ```c# AppsFlyer.initSDK(devKey, appID, this); AppsFlyer.enableTCFDataCollection(true); //YOUR_CMP_FLOW() // if already has consent ready - you can start AppsFlyer.startSDK(); //else Waiting for CMP completion and data ready and then start AppsFlyer.startSDK(); ``` ### Manually collect consent data 1. Initialize the SDK. 2. Determine whether the GDPR applies or not to the user. ### When GDPR applies to the user 1. Given that GDPR is applicable to the user, determine whether the consent data is already stored for this session. i. If there is no consent data stored, show the consent dialog to capture the user consent decision. ii. If there is consent data stored continue to the next step. 2. To transfer the consent data to the SDK create an AppsFlyerConsent object with the following parameters: - hasConsentForDataUsage - Indicates whether the user has consented to use their data for advertising purposes. - hasConsentForAdsPersonalization - Indicates whether the user has consented to use their data for personalized advertising. 3. Call setConsentData()with the AppsFlyerConsent object. 5. Call start(). ```c# // If the user is subject to GDPR - collect the consent data // or retrieve it from the storage ... // Set the consent data to the SDK: AppsFlyerConsent consent = AppsFlyerConsent.ForGDPRUser(true, true); AppsFlyer.setConsentData(consent); AppsFlyer.startSDK(); ``` ### When GDPR does not apply to the user 1. Create an AppsFlyerConsent object using the ForNonGDPRUser() initializer. This initializer doesn’t accept any parameters. 2. Pass the empty AppsFlyerConsent object to setConsentData(). 2. Call start(). ```c# // If the user is not subject to GDPR: AppsFlyerConsent consent = AppsFlyerConsent.ForNonGDPRUser(); AppsFlyer.setConsentData(consent); AppsFlyer.startSDK(); ``` ## Verify consent data is sent To test whether your SDK sends DMA consent data with each event, perform the following steps: 1. Enable the SDK debug mode. 2. Search for consent_data in the log of the outgoing request. for more information visit [iOS](https://dev.appsflyer.com/hc/docs/ios-send-consent-for-dma-compliance) [Android](https://dev.appsflyer.com/hc/docs/android-send-consent-for-dma-compliance) --- ## Sending SKAN postback to Appsflyer To register the AppsFlyer endpoint, you need to add the `NSAdvertisingAttributionReportEndpoint` key to your info.plist and set the value to `https://appsflyer-skadnetwork.com/`. More info on how to update the info.plist can be found [here](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/docs/Troubleshooting.md#updating-the-infoplist). --- ## MacOS initialization 1. Use the prefab `AppsFlyerObject` 2. Add your MacOS app id 3. Build for the platform `PC, Mac & Linux Standelone` and choose `MacOS` as the target platform. --- ## Request Listeners (Optional) 1. Attach the 'AppsFlyer.cs' script to the game object with the AppsFlyer init code. (AppsFlyerObject, ect) 2. Add the following code **before** startSDK() Sessions response example: ```c# void Start() { AppsFlyer.OnRequestResponse += AppsFlyerOnRequestResponse; AppsFlyer.initSDK(devKey, appID, this); AppsFlyer.startSDK(); } void AppsFlyerOnRequestResponse(object sender, EventArgs e) { var args = e as AppsFlyerRequestEventArgs; AppsFlyer.AFLog("AppsFlyerOnRequestResponse", " status code " + args.statusCode); } ``` In-App response example: ```c# void Start() { AppsFlyer.OnInAppResponse += (sender, args) => { var af_args = args as AppsFlyerRequestEventArgs; AppsFlyer.AFLog("AppsFlyerOnRequestResponse", " status code " + af_args.statusCode); }; AppsFlyer.initSDK(devKey, appID, this); AppsFlyer.startSDK(); } ``` | statusCode | errorDescription | | ----------- | ----------- | | 200 | null | | 10 | "Event timeout. Check 'minTimeBetweenSessions' param" | | 11 | "Skipping event because 'isStopTracking' enabled" | | 40 | Network error: Error description comes from Android | | 41 | "No dev key" | | 50 | "Status code failure" + actual response code from the server | --- ================================================ FILE: docs/DMAConsent.md ================================================ --- title: Sending Consent Data for DMA Compliance category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 4 hidden: false --- # Send consent for DMA compliance (Unity) For a general introduction to DMA consent data, see the [DMA consent overview](https://dev.appsflyer.com/hc/docs/send-consent-for-dma-compliance) in the AppsFlyer docs. The SDK offers two alternative methods for gathering consent data: - **Through a Consent Management Platform (CMP):** If your app uses a CMP that complies with the IAB **Transparency and Consent Framework (TCF) v2.2/2.3**, the SDK can automatically retrieve consent details. **OR** - **Through a dedicated SDK API:** Provide Google’s required consent data directly to the SDK using the **Unity** consent APIs. > **Note** > Use **only one** method per specific event. If both are supplied for the same event, AppsFlyer prioritizes the manual consent data sent via the dedicated API. --- ## Use CMP to collect consent data A CMP compatible with **TCF v2.2/2.3** persists the consent strings on-device (NSUserDefaults on iOS / SharedPreferences on Android). To enable the Unity SDK to access this data and include it with every event, follow these steps: 1. **Initialize** the SDK (in your Unity scene startup code). 2. **Before** starting the SDK, call `AppsFlyer.enableTCFDataCollection(true)` to instruct the SDK to collect TCF data from the device. 3. Use your CMP to decide if the consent dialog is needed in the current session. 4. If needed, **show the consent dialog** to capture the user’s decision; otherwise skip to step 6. 5. After the CMP confirms the user made a decision and the TCF data is stored, proceed. 6. Call `AppsFlyer.startSDK()`. ### Unity (C#) example ```csharp using AppsFlyerSDK; using UnityEngine; public class DMAConsentCmpFlow : MonoBehaviour { [SerializeField] string devKey = "AF_DEV_KEY"; [SerializeField] string appId = "com.example.app"; // empty on Android void Start() { AppsFlyer.initSDK(devKey, appId); AppsFlyer.enableTCFDataCollection(true); // collect TCF strings from device // Pseudocode for your CMP flow: if (CmpManager.HasConsentReady()) { AppsFlyer.startSDK(); } else { CmpManager.ShowDialog(onUserDecided: () => { // CMP indicates consent data now stored in device prefs AppsFlyer.startSDK(); }); } } } ``` --- ## Manually collect consent data If your app **does not** use a TCF v2.2/2.3-compatible CMP, provide the consent data directly to the SDK via `AppsFlyer.setConsentData(AppsFlyerConsent)`. 1. **Initialize** the SDK in your startup flow. 2. Determine whether **GDPR applies** to the user. 3. Check whether consent data is **already stored** for this session. - If not stored, **show your own consent dialog** and collect the user’s choices. - If stored, continue. 4. Create an `AppsFlyerConsent` instance with the following parameters, then call `AppsFlyer.setConsentData(consent)`: - `isUserSubjectToGDPR` – whether GDPR applies to this user. - `hasConsentForDataUsage` – consent to use data for advertising/measurement. - `hasConsentForAdsPersonalization` – consent for personalized ads. - `hasConsentForAdStorage` – consent to store/access information on the device. 5. If GDPR **does not apply**, set `isUserSubjectToGDPR = false` and **the rest to `null`**. 6. Call `AppsFlyer.startSDK()`. ### Unity (C#) examples **GDPR applies (example values):** ```csharp // Build from your dialog results var consent = new AppsFlyerConsent( isUserSubjectToGDPR: true, hasConsentForDataUsage: true, hasConsentForAdsPersonalization:true, hasConsentForAdStorage: false ); AppsFlyer.setConsentData(consent); AppsFlyer.startSDK(); ``` **GDPR does NOT apply:** ```csharp var nonGdpr = new AppsFlyerConsent( isUserSubjectToGDPR: false, hasConsentForDataUsage: null, hasConsentForAdsPersonalization:null, hasConsentForAdStorage: null ); AppsFlyer.setConsentData(nonGdpr); AppsFlyer.startSDK(); ``` > **Note** > The SDK registers only the parameters you explicitly pass via the `AppsFlyerConsent` object. (Unset parameters remain unknown.) --- ## Verify consent data is sent To confirm the SDK sends DMA consent data with each event: 1. **Enable debug mode** in the Unity Plugin (development only). 2. Inspect the device logs for the outgoing request and look for a `consent_data` section. - **CMP flow** includes a `tcf` block (e.g., `gdpr_applies`, `tcstring`, `cmp_sdk_id`, `policy_version`). - **Manual flow** includes a `manual` block (e.g., `gdpr_applies`, `ad_user_data_enabled`, `ad_personalization_enabled`). Example patterns from Android/iOS logs (your Unity build will emit analogous payloads per platform): - CMP flow: `..."consent_data":{"tcf":{"policy_version":4,"cmp_sdk_id":...,"gdpr_applies":1,"tcstring":"..."}}...` - Manual flow: `..."consent_data":{"manual":{"gdpr_applies":true,"ad_user_data_enabled":true,"ad_personalization_enabled":true}}...` --- ## Quick API reference (Unity) - **Collect TCF strings automatically:** `AppsFlyer.enableTCFDataCollection(true|false)` — call **before** `startSDK()`. - **Send manual consent:** `AppsFlyer.setConsentData(AppsFlyerConsent consent)` with: `AppsFlyerConsent(bool? isUserSubjectToGDPR, bool? hasConsentForDataUsage, bool? hasConsentForAdsPersonalization, bool? hasConsentForAdStorage)` (use `null` where unknown/not applicable). --- ### Notes & best practices - Set consent **before** `startSDK()` so the first session includes the correct signals. - If the user changes their choice later, rebuild `AppsFlyerConsent` with the new values and call `setConsentData` again. ================================================ FILE: docs/DeepLinkIntegrate.md ================================================ --- title: Deep Linking - Installation category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 6 hidden: false --- # Getting started ![alt text](https://massets.appsflyer.com/wp-content/uploads/2018/03/21101417/app-installed-Recovered.png "") ## Deep Linking Types: 1. **Deferred Deep Linking** - Serving personalized content to new or former users, directly after the installation. 2. **Direct Deep Linking** - Directly serving personalized content to existing users, which already have the mobile app installed. ** Unified deep linking (UDL) ** - an API which enables you to send new and existing users to a specific in-app activity as soon as the app is opened. For more info please check out the [OneLink™ Deep Linking Guide](https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-Deep-Linking-Guide#Intro) and [developer guide](https://dev.appsflyer.com/hc/docs/getting-started-1). --- # Android Deeplink Setup ## App Links For more on App Links check out the guide [here](https://dev.appsflyer.com/hc/docs/initial-setup-for-deep-linking-and-deferred-deep-linking#procedures-for-android-app-links). ## URI scheme in Android Full setup guide for URI scheme in Android can be found [here](https://dev.appsflyer.com/hc/docs/initial-setup-for-deep-linking-and-deferred-deep-linking#procedures-for-uri-scheme). # iOS Deeplink Setup ## Universal Links Essentially, the Universal Links method links between an iOS mobile app and an associate website/domain, such as AppsFlyer’s OneLink domain (xxx.onelink.me). For more on Universal Links check out the guide [here](https://dev.appsflyer.com/hc/docs/dl_ios_init_setup#procedures-for-ios-universal-links). ## URI scheme in iOS Full setup guide for URI scheme in iOS can be found [here](https://dev.appsflyer.com/hc/docs/dl_ios_init_setup#procedures-for-uri-scheme). ================================================ FILE: docs/InAppEvents.md ================================================ --- title: In-App Events category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 3 hidden: false --- - [Overview](#overview) - [Send Event](#send-event) - [In-app purchase validation (V2)](#in-app-purchase-validation-v2) - [In-app purchase validation (legacy)](#in-app-purchase-validation) ## Overview In-App Events provide insight on what is happening in your app. It is recommended to take the time and define the events you want to measure to allow you to measure *ROI* (Return on Investment) and *LTV* (Lifetime Value). Recording in-app events is performed by calling `sendEvent` with event name and value parameters. See In-App Events [documentation](https://support.appsflyer.com/hc/en-us/articles/115005544169-Rich-in-app-events-for-Android-and-iOS#introduction-predefined-and-custom-events) for more details. Find more info about recording events [here](https://dev.appsflyer.com/hc/docs/in-app-events-sdk). ## Send Event `void sendEvent(string eventName, Dictionary eventValues)` | parameter | type | description | | ----------- |----------------------------- |------------------------------------------ | | `eventName` | `string` | The name of the event | | `eventValues` | `Dictionary` | The event values that are sent with the event | *Example:* ```c# Dictionary eventValues = new Dictionary(); eventValues.Add(AFInAppEvents.CURRENCY, "USD"); eventValues.Add(AFInAppEvents.REVENUE, "0.99"); eventValues.Add("af_quantity", "1"); AppsFlyer.sendEvent(AFInAppEvents.PURCHASE, eventValues); ``` *** ## Logging revenue > 📘 Note > > For events with **revenue**, including in-app purchases, subscriptions, and ad revenue events, AppsFlyer customers with an ROI360 subscription should avoid using the `AFInAppEvents.REVENUE`(`af_revenue`) parameter in their in-app events. Doing so can result in duplicate revenue being reported. Instead, they should utilize the [purchase connector](https://dev.appsflyer.com/hc/docs/purchaseconnectorunity) and the [ad revenue SDK API](https://dev.appsflyer.com/hc/docs/ad-revenue-unity). You can send revenue with any in-app event. Use the `AFInAppEvents.REVENUE` event parameter to include revenue in the in-app event. You can populate it with any numeric value, positive or negative. The revenue value should not contain comma separators, currency signs, or text. A revenue event should be similar to 1234.56, for example. Currency code requirements when sending revenue events - Default currency: USD - Use a [3-character ISO 4217 code](https://en.wikipedia.org/wiki/ISO_4217#Active_codes) (an example follows). - Set the currency code by calling the API: ```c# AppsFlyer.setCurrencyCode("ZZZ") ``` **Example: In-app purchase event with revenue** This purchase event is for 200.12 Euros. For the revenue to reflect in the dashboard use the following. ```c# using System.Collections.Generic; Dictionary purchaseEvent = new Dictionary (); purchaseEvent.Add(AFInAppEvents.CURRENCY, "EUR"); purchaseEvent.Add(AFInAppEvents.REVENUE, "200.12"); purchaseEvent.Add(AFInAppEvents.QUANTITY, "1"); purchaseEvent.Add(AFInAppEvents.CONTENT_TYPE, "category_a",); AppsFlyer.sendEvent ("af_purchase", purchaseEvent); ``` > 📘 Note > Do not add currency symbols to the revenue value. ### Logging negative revenue Record negative revenue using a minus sign. - Revenue value is preceded by a minus sign. - The event name has a unique value, "cancel_purchase". This lets you identify negative revenue events in raw data reports and in the Dashboard. **Example: App user receives a refund or cancels a subscription** ```c# using System.Collections.Generic; Dictionary purchaseEvent = new Dictionary (); purchaseEvent.Add(AFInAppEvents.CURRENCY, "USD"); purchaseEvent.Add(AFInAppEvents.REVENUE, "-200"); purchaseEvent.Add(AFInAppEvents.QUANTITY, "1"); purchaseEvent.Add(AFInAppEvents.CONTENT_TYPE, "category_a"); AppsFlyer.sendEvent ("cancel_purchase", purchaseEvent); ``` ## In-app purchase validation (V2) For In-App Purchase Receipt Validation, follow the instructions according to your operating system. **Notes** - Calling validateReceipt automatically generates an `af_purchase` in-app event, so you don't need to send this event yourself. - The validate purchase response is triggered in the `AppsFlyerTrackerCallbacks.cs` class. - **iOS Error Handling:** On iOS, the `onValidateAndLogFailure` callback is only triggered for actual errors (network errors, invalid parameters, etc.). Validation failures (e.g., invalid receipt) are returned through the `onValidateAndLogComplete` callback in the response dictionary. Check the response to determine if validation was successful. ```c# // for Android `void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary additionalParameters, MonoBehaviour gameObject)` // for iOS `void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary extraEventValues, MonoBehaviour gameObject)` ``` ```c# using UnityEngine.Purchasing; using AppsFlyerSDK; public class AppsFlyerObject : MonoBehaviour, IAppsFlyerValidateAndLog { public static string kProductIDConsumable = "com.test.cons"; void Start() { AppsFlyer.initSDK("devKey", "devKey"); AppsFlyer.startSDK(); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { string prodID = args.purchasedProduct.definition.id; string price = args.purchasedProduct.metadata.localizedPrice.ToString(); string currency = args.purchasedProduct.metadata.isoCurrencyCode; string receipt = args.purchasedProduct.receipt; var recptToJSON = (Dictionary)AFMiniJSON.Json.Deserialize(product.receipt); var receiptPayload = (Dictionary)AFMiniJSON.Json.Deserialize((string)recptToJSON["Payload"]); var transactionID = product.transactionID; if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal)) { #if UNITY_IOS if(isSandbox) { AppsFlyeriOS.setUseReceiptValidationSandbox(true); } AFSDKPurchaseDetailsIOS details = AFSDKPurchaseDetailsIOS.Init(prodID, price, currency, transactionID); AppsFlyeriOS.validateAndSendInAppPurchase(details, null, this); #elif UNITY_ANDROID AFPurchaseDetailsAndroid details = new AFPurchaseDetailsAndroid(AFPurchaseType.Subscription, "token", prodID, price, currency); AppsFlyerAndroid.validateAndSendInAppPurchase( details, null, this); #endif } return PurchaseProcessingResult.Complete; } public void onValidateAndLogComplete(string result) { AppsFlyer.AFLog("onValidateAndLogComplete", result); Dictionary validateAndLogDataDictionary = AppsFlyer.CallbackStringToDictionary(result); // On iOS: Check the response dictionary to determine if validation was successful // Validation failures are returned here, not in onValidateAndLogFailure } public void onValidateAndLogFailure(string error) { AppsFlyer.AFLog("onValidateAndLogFailure", error); Dictionary validateAndLogErrorDictionary = AppsFlyer.CallbackStringToDictionary(error); // On iOS: This is only called for actual errors (network errors, invalid parameters, etc.) // Validation failures come through onValidateAndLogComplete instead } } ``` ## In-app purchase validation > ⚠️ **Deprecated:** This legacy API is deprecated. Use the [V2 version](#in-app-purchase-validation-v2) with structured data classes (`AFPurchaseDetailsAndroid`/`AFSDKPurchaseDetailsIOS`) instead. The legacy methods are maintained for backward compatibility only. For In-App Purchase Receipt Validation, follow the instructions according to your operating system. **Notes** Calling validateReceipt automatically generates an `af_purchase` in-app event, so you don't need to send this event yourself. The validate purchase response is triggered in the `AppsFlyerTrackerCallbacks.cs` class. `void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string tranactionId, Dictionary additionalParameters, MonoBehaviour gameObject)` ```c# //To get the callbacks //AppsFlyer.createValidateInAppListener ("AppsFlyerTrackerCallbacks", "onInAppBillingSuccess", "onInAppBillingFailure"); AppsFlyer.validateReceipt(string publicKey, string purchaseData, string signature, string price, string currency, Dictionary additionalParametes); ``` ```c# using UnityEngine.Purchasing; using AppsFlyerSDK; public class AppsFlyerObject : MonoBehaviour, IStoreListener, IAppsFlyerValidateReceipt { public static string kProductIDConsumable = "com.test.cons"; void Start() { AppsFlyer.initSDK("devKey", "devKey"); AppsFlyer.startSDK(); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { string prodID = args.purchasedProduct.definition.id; string price = args.purchasedProduct.metadata.localizedPrice.ToString(); string currency = args.purchasedProduct.metadata.isoCurrencyCode; string receipt = args.purchasedProduct.receipt; var recptToJSON = (Dictionary)AFMiniJSON.Json.Deserialize(product.receipt); var receiptPayload = (Dictionary)AFMiniJSON.Json.Deserialize((string)recptToJSON["Payload"]); var transactionID = product.transactionID; if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal)) { #if UNITY_IOS if(isSandbox) { AppsFlyeriOS.setUseReceiptValidationSandbox(true); } AppsFlyeriOS.validateAndSendInAppPurchase(prodID, price, currency, transactionID, null, this); #elif UNITY_ANDROID var purchaseData = (string)receiptPayload["json"]; var signature = (string)receiptPayload["signature"]; AppsFlyerAndroid.validateAndSendInAppPurchase( "", signature, purchaseData, price, currency, null, this); #endif } return PurchaseProcessingResult.Complete; } public void didFinishValidateReceipt(string result) { AppsFlyer.AFLog("didFinishValidateReceipt", result); } public void didFinishValidateReceiptWithError(string error) { AppsFlyer.AFLog("didFinishValidateReceiptWithError", error); } } ``` ================================================ FILE: docs/Installation.md ================================================ --- title: Installation category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 0 hnameden: false --- # Adding appsflyer-unity-plugin to your project ## Adding the SDK to your project In order to add the plugin to your project, you can either add the *unitypackage* **or** use *Unity Package Manager*. - [Installation adding the unitypackage](#using-unitypackage) - [Installation using Unity Package Manager](#using-unity-package-manager) **Note:** The plugin is built with [The External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver) (EDM4U) (formerly Play Services Resolver / Jar Resolver) * The External Dependency Manager for Unity is distributed with the `appsflyer-unity-plugin` by default. * This will ease the integration process, by resolving dependency conflicts between your plugin and other plugins in your project. * Adding the `appsflyer-unity-plugin.v*.unitypackage` will automatically import all the assets required for both the AppsFlyer SDK and the External Dependency Manager for Unity. ## Using unitypackage 1. Clone / download the [plugin repository](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin). 2. [Import](https://docs.unity3d.com/Manual/AssetPackages.html) the `appsflyer-unity-plugin-*.unitypackage` or `appsflyer-unity-plugin-strict-mode.*.unitypackage` file from the `strict-mode-sdk` folder for the Strict version of the plugin, into your Unity project. 3. Go to Assets >> Import Package >> Custom Package. 4. Select the `appsflyer-unity-plugin-*.unitypackage` file or the `appsflyer-unity-plugin-strict-mode.*.unitypackage` file from the `strict-mode-sdk` folder for the Strict version of the plugin. **Note:** If you do not wish to include [The External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver), refer to the steps of this [installation](#installation-without-unity-jar-resolver). ## Using Unity Package Manager 1. Follow Google's [guide](https://developers.google.com/unity/instructions) in order to integrate UPM (Unity Package Manager). **Note:** If you do not wish to include [The External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver), refer to steps 2 & 3 [here](#installation-without-unity-jar-resolver). 4. Add appsflyer-unity-plugin in the dependency : Add this line for the latest version of the regular mode ``` "appsflyer-unity-plugin": "https://github.com/AppsFlyerSDK/appsflyer-unity-plugin.git#upm" ``` Or this line for latest version of the Strict mode : ``` "appsflyer-unity-plugin": "https://github.com/AppsFlyerSDK/appsflyer-unity-plugin.git#Strict-upm" ``` 5. Download the [External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver) to be able to resolve our Android / iOS dependencies. **Note:** To choose an earlier version and not the latest, you can replace the `upm` or `Strict-upm` with the specific version, `v6.10.30` for the regular version of 6.10.30 or `Strict-v6.10.30` for the Strict version of 6.10.30. --- # Installation without unity-jar-resolver * If you do not wish to include [The External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver) then follow these steps: 1. import `appsflyer-unity-plugin.v*.unitypackage` to your project but make sure to uncheck the `EDM4U` dependencies. 2. Download and add the required Android dependencies to the Assets/Plugins/Android folder: 1. [AppsFlyer Android SDK](https://repo1.maven.org/maven2/com/appsflyer/af-android-sdk/6.17.6/af-android-sdk-6.17.6.aar) 2. [AppsFlyer Unity Wrapper](https://repo1.maven.org/maven2/com/appsflyer/unity-wrapper/6.17.90/unity-wrapper-6.17.90.aar) — Billing Library 7, or [unity-wrapper-6.17.91](https://repo1.maven.org/maven2/com/appsflyer/unity-wrapper/6.17.91/unity-wrapper-6.17.91.aar) for Billing Library 8 3. [Google Installreferrer library](https://mvnrepository.com/artifact/com.android.installreferrer/installreferrer/2.1) 3. Download and add the required iOS dependencies to the Assets/Plugins/iOS/AppsFlyer folder: 1. [Download](https://github.com/AppsFlyerSDK/AppsFlyerFramework/releases/tag/6.17.9) the iOS SDK as a static library `AppsFlyerLib.xcframework.zip` 2. Unzip the file you downloaded 3. Drag & drop all the files into the `Assets/Plugins/iOS/AppsFlyer` folder ================================================ FILE: docs/Introduction.md ================================================ --- title: Introduction slug: "introduction-unity" category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 0 hidden: false --- # appsflyer-unity-plugin [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![GitHub tag](https://img.shields.io/github/v/release/AppsFlyerSDK/appsflyer-unity-plugin)](https://img.shields.io/github/v/release/AppsFlyerSDK/appsflyer-unity-plugin) [![Unit tests](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/actions/workflows/main.yml/badge.svg)](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/actions/workflows/main.yml) [![check packages](https://github.com/af-margot/appsflyer-unity-plugin-beta/actions/workflows/checksums_files.yml/badge.svg)](https://github.com/af-margot/appsflyer-unity-plugin-beta/actions/workflows/checksums_files.yml) 🛠 In order for us to provide optimal support, please contact AppsFlyer support through the Customer Assistant Chatbot for assistance with troubleshooting issues or product guidance.
To do so, please follow [this article](https://support.appsflyer.com/hc/en-us/articles/23583984402193-Using-the-Customer-Assistant-Chatbot) ## ButterFlyer Sample App ButterFlyer - Unity Sample App ![](https://files.readme.io/3cdc241-banner-butterflyer.png "banner-butterflyer.png") ## Plugin Github Repository > 📘 Github repository for this plugin is [here](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin) ### This plugin is built for - Android AppsFlyer SDK v6.17.5 - Android Purchase Connector 2.2.0 - iOS AppsFlyer SDK v6.17.8 - iOS Purchase Connector 6.17.8 --- ## 📌 Important: Two Versions of Unity Plugin v6.17.7 We have released **two** versions of the AppsFlyer Unity plugin to support teams at different stages of migrating to **Google Play Billing Library v8.0.0**. ### Option A — `v6.17.81` (Billing Library v8) - **What's included:** Support for **Google Play Billing Library 8.0.0** on Android (Android Purchase Connector version - 2.2.0). - **Impact:** This version may introduce **breaking changes** for apps that have **not yet migrated** to the Billing v8 APIs. - **Unity IAP requirement:** If you choose this option, update **Unity IAP (`com.unity.purchasing`) to version 5.0.0 or newer** (we recommend the latest 5.x). Unity IAP 4.x does **not** include Billing v8. ### Option B — `v6.17.80` (Billing Library v7) - **Purpose:** For developers **not ready** to adopt Billing v8. - **Bundled SDKs:** **iOS SDK 6.17.8** and **Android SDK 6.17.5**. - **Android Purchase Connector:** Version 2.1.2 - **Impact:** Lets you update the AppsFlyer SDKs without changing your existing (pre‑v8) billing integration. --- ## 🎉 New in 6.17.1 - Purchase Connector Integration - Starting from version 6.17.1, the **Purchase Connector is now integrated directly into the main AppsFlyer Unity plugin**. You no longer need to download, import, or maintain a separate Purchase Connector package. - If you were previously using the standalone Purchase Connector from a separate repository, simply remove any references to `using AppsFlyerConnector;` from your codebase, as its functionality is now included in the main plugin under the `AppsFlyerSDK` namespace. - The Purchase Connector now supports **StoreKit 2** for iOS 15+ alongside the existing StoreKit 1 support. - For detailed migration instructions and new features, see our [Purchase Connector documentation](/docs/purchase-connector.md). --- ## ❗❗ Breaking changes when updating to 6.17.5 ❗❗ - **In-App Purchase Validation API Changes**: The `validateAndSendInAppPurchase` method signatures have been updated for better type safety and cleaner code. - **V2 Methods (Recommended)**: New overloads using structured data classes (`AFPurchaseDetailsAndroid`/`AFSDKPurchaseDetailsIOS`) are now the recommended approach. - **Legacy Methods (Deprecated)**: The old string-based parameter methods are now deprecated but maintained for backward compatibility. - **Migration Required**: If you're using the old `validateAndSendInAppPurchase` methods, consider migrating to the V2 versions for better maintainability. - For detailed API documentation and migration examples, see our [API reference](/docs/API.md). --- ## ❗❗ Breaking changes when updating to 6.12.20 ❗❗ - Starting from version 6.12.20, we have changed the way we distribute the plugin via UPM. The UPM branches will no longer hold a dependency for `com.google.external-dependency-manager` as it was proved to cause issues in different versions of Unity - to be clear, this dependency is still required to utilize our plugin, we just can't distribute the plugin with it in UPM form as the EDM4U dependency is [not available via UPM for quite a while already](https://github.com/googlesamples/unity-jar-resolver/issues/434#issuecomment-827028132) but is still available via `.unitypackage` or `.tgz` files, if you use UPM to fetch our plugin - [please download a suitable version of EDM4U](https://github.com/googlesamples/unity-jar-resolver) so you will be able to resolve the dependencies, or opt for [an installation without EDM4U](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/docs/Installation.md#installation-without-unity-jar-resolver). --- ## ❗❗ Breaking changes when updating to 6.6.0 ❗❗ - Starting version 6.6.0, there is no more need to differentiate between iOS and Android APIs. All APIs must be called with `AppsFlyer` class (even if the API is only iOS or Android). - Please take into consideration that since version 6.6.0, most of the APIs require `initSDK` to be called prior to using them, and since version 6.10.10 only a handful of APIs will properly work when called prior to initialization: `setIsDebug`, `setCurrencyCode`, `setHost`, `disableSKAdNetwork`. Example: Before 6.6.0: ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyeriOS.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` --- After 6.6.0: ```c# #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.waitForATTUserAuthorizationWithTimeoutInterval(60); #endif ``` --- ## Strict Mode The plugin supports a Strict Mode which completely removes the IDFA collection functionality and AdSupport framework dependencies. Use the Strict Mode when developing apps for kids, for example. More information about how to install the Strict Mode is available [here](/docs/Installation.md). ### AD_ID permission for Android In v6.8.0 of the AppsFlyer SDK, we added the normal permission com.google.android.gms.permission.AD_ID to the SDK's AndroidManifest, to allow the SDK to collect the Android Advertising ID on apps targeting API 33. If your app is targeting children, you need to revoke this permission to comply with Google's Data policy. You can read more about it [here](https://dev.appsflyer.com/hc/docs/install-android-sdk#the-ad_id-permission). --- ## 🚀 Getting Started - [Installation](/docs/Installation.md) - [Integration](/docs/BasicIntegration.md) - [Test integration](/docs/Testing.md) - [In-app events](/docs/InAppEvents.md) - [Send Consent for DMA Compliance](/docs/DMAConsent.md) - [Uninstall measurement](/docs/UninstallMeasurement.md) ## 💰 Purchase Connector - [Purchase Connector (ROI360)](/docs/purchase-connector.md) ## 🔗 Deep Linking - [Integration](/docs/DeepLinkIntegrate.md) - [Unified Deep Link (UDL)](/docs/UnifiedDeepLink.md) - [User invite](/docs/UserInvite.md) ## 🧪 Sample App - [ButterFlyer](https://github.com/AppsFlyerSDK/appsflyer-unity-sample-app) ---- ### [API reference](/docs/API.md) ### [Troubleshooting](/docs/Troubleshooting.md) ================================================ FILE: docs/MigrationGuide.md ================================================ --- title: Migration guide from v4 category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 11 hidden: false --- 1. [Remove the old Plugin](#remove-the-old-plugin) 2. [Init the new Plugin](#init-the-new-plugin) 3. [Update deeplink logic](#update-deeplink-logic) 4. [Update code](#update-other-code) :warning: There are breaking changes when migrating to Unity v5. This includes: * New class names * New android package name * `com.appsflyer.GetDeepLinkingActivity` does not exist in Unity v5. This is no longer required for deeplinking * unity-jar-resolver is used to import assets # Remove the old plugin 1. Remove all the items contained in `AppsFlyerUnityPlugin_v4.x.x.unitypackage` Here is a list of all the filed included: ``` Assets/Plugins/AppsFlyer.cs Assets/Plugins/AFInAppEvents.cs Assets/Plugins/AppsFlyerTrackerCallbacks.cs --- Assets/Plugins/Android/AppsFlyerAndroidPlugin.jar Assets/Plugins/Android/AF-Android-SDK.jar Assets/Plugins/Android/installreferrer-1.0.aar --- Assets/Plugins/iOS/AppsFlyerAppController.mm Assets/Plugins/iOS/AppsFlyerCrossPromotionHelper.h Assets/Plugins/iOS/AppsFlyerDelegate.h Assets/Plugins/iOS/AppsFlyerDelegate.mm Assets/Plugins/iOS/AppsFlyerLinkGenerator.h Assets/Plugins/iOS/AppsFlyerShareInviteHelper.h Assets/Plugins/iOS/AppsFlyerTracker.h Assets/Plugins/iOS/AppsFlyerWrapper.h Assets/Plugins/iOS/AppsFlyerWrapper.mm Assets/Plugins/iOS/libAppsFlyerLib.a ``` # Init the new plugin 1. Add the new .unitypackage, which can be found in the new plugin. 2. There are two main options of initialization: 1. Remove all old init code and use the new .prefab object. 2. Update your existing init code. ## 1. remove all old init code To do this simpily remove the game object or all the appsflyer code in the game object where there sdk is being initalized. Then follow the init guide for the new plugin. ## 2. Update old init code with new code Replace old init code: ```c# void Start () { AppsFlyer.setAppsFlyerKey("K2***********99"); /* AppsFlyer.setIsDebug(true); */ #if UNITY_IOS AppsFlyer.setAppID("41******85"); AppsFlyer.trackAppLaunch(); AppsFlyer.getConversionData(); #elif UNITY_ANDROID AppsFlyer.setAppID ("com.appsflyer.test"); AppsFlyer.init("K2**********99","AppsFlyerTrackerCallbacks"); #endif } ``` With new init code: ```c# using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour , IAppsFlyerConversionData { void Start() { /* AppsFlyer.setDebugLog(true); */ AppsFlyer.init-sdk("devkey", "appID", this); AppsFlyer.startSDK(); } // ..... } ``` **Important** If you are also implementing conversion data and/or deeplinking then you need to initialize the SDK with the `IAppsFlyerConversionData` interface. ## Update deeplink logic Unity v5 does not include `com.appsflyer.GetDeepLinkingActivity`.
This was used in Unity v4 as a workaround for deeplinking.
If you are using this class for deeplinking, then make sure to remove the GetDeepLinkingActivity from the AndroidManifest.xml file. ## Update other Code Here is a list of all the old API, and the new API. **API** - [setAppsFlyerKey](#init-sdk) - [trackAppLaunch](#init-sdk) - [setAppID](#init-sdk) - [getConversionData](#init-sdk) - [init](#init-sdk) - [loadConversionData](#init-sdk) - [setCurrencyCode](#api-that-did-not-change) - [setCustomerUserID](#api-that-did-not-change) - [setAdditionalData](#api-that-did-not-change) - [trackCrossPromoteImpression](#api-that-did-not-change) - [setMinTimeBetweenSessions](#api-that-did-not-change) - [setHost](#api-that-did-not-change) - [setUserEmails](#api-that-did-not-change) - [setResolveDeepLinkURLs](#api-that-did-not-change) - [setOneLinkCustomDomain](#api-that-did-not-change) - [trackRichEvent](#updated-core-api) - [stopTracking](#updated-core-api) - [setIsDebug](#api-that-did-not-change) - [getAppsFlyerId](#api-that-did-not-change) - [setDeviceTrackingDisabled](#updated-core-api) - [setAppInviteOneLinkID](#api-that-did-not-change) - [generateUserInviteLink](#updated-core-api) - [trackAndOpenStore](#updated-core-api) - [setIsSandbox](#ios-only-api) - [registerUninstall](#ios-only-api) - [setCollectIMEI](#android-only-api) - [setCollectAndroidID](#android-only-api) - [setImeiData](#android-only-api) - [updateServerUninstallToken](#android-only-api) - [setAndroidIdData](#android-only-api) - [setPreinstallAttribution](#android-only-api) - [validate-receipt (ios)](#validate-receipt) - [validate-receipt (android)](#validate-receipt) - [createValidateInAppListener](#validate-receipt) - [handlePushNotification](#deprecated) - [enableUninstallTracking](#deprecated) - [handleOpenUrl](#deprecated) - [getHost](#deprecated) - [loadConversionData](#deprecated) - [setGCMProjectNumber](#deprecated) - [setShouldCollectDeviceName](#deprecated) ## Init SDK ```c# // Old API's AppsFlyer.setAppsFlyerKey(string key); AppsFlyer.trackAppLaunch(); AppsFlyer.setAppID(string appleAppId); AppsFlyer.getConversionData (); AppsFlyer.init(string devKey); AppsFlyer.init(string devKey, string callbackObject); AppsFlyer.loadConversionData(string callbackObject); // New API's AppsFlyer.initSDK(string key, string app_id); // without deeplinking/conversion data AppsFlyer.initSDK(string key, string app_id, MonoBehaviour gameObject); // with deeplinking/conversion data AppsFlyer.startSDK(); ``` ## API that did not change ```c# AppsFlyer.setCurrencyCode(string currencyCode); AppsFlyer.setCustomerUserID(string customerUserID); AppsFlyer.setAdditionalData(Dictionary extraData); AppsFlyer.trackCrossPromoteImpression(string appId, string campaign); AppsFlyer.setMinTimeBetweenSessions(int seconds); AppsFlyer.setHost(string hostPrefixName, string hostName); AppsFlyer.setUserEmails(EmailCryptType cryptType, params string[] userEmails); AppsFlyer.setResolveDeepLinkURLs(params string[] userEmails); AppsFlyer.setOneLinkCustomDomain(params string[] domains); AppsFlyer.setIsDebug(bool isDebug); AppsFlyer.getAppsFlyerId(); AppsFlyer.setAppInviteOneLinkID(string oneLinkID); ``` ## Updated core API ```c# // old AppsFlyer.trackRichEvent(string eventName, Dictionary eventValues); // new AppsFlyer.sendEvent(string eventName, Dictionary eventValues); // old AppsFlyer.stopTracking(bool isStopTracking); // new AppsFlyer.stopSDK(bool isStopTracking); // old AppsFlyer.setDeviceTrackingDisabled(bool state); // new AppsFlyer.anonymizeUser(true); // old AppsFlyer.generateUserInviteLink(Dictionary parameters, string callbackObject,string callbackMethod, string callbackFailedMethod); // new AppsFlyer.generateUserInviteLink(Dictionary parameters, MonoBehaviour gameObject); // old AppsFlyer.trackAndOpenStore(string promotedAppId, string campaign, Dictionary customParams); // new AppsFlyer.trackAndOpenStore(string appID, string campaign, Dictionary userParams, MonoBehaviour gameObject); ``` ## iOS Only API ```c# // old AppsFlyer.setIsSandbox(bool isSandbox); // new #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.setUseReceiptValidationSandbox(true); #endif // old AppsFlyer.registerUninstall(byte[] token); // new #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.registerUninstall(token); #endif // old AppsFlyer.handleOpenUrl(string url, string sourceApplication, string annotation); // new #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.handleOpenUrl(string url, string sourceApplication, string annotation); #endif ``` ## Android Only API ```c# // old AppsFlyer.setCollectIMEI(bool shouldCollect); // new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCollectIMEI(bool shouldCollect); #endif // old AppsFlyer.setCollectAndroidID(bool shouldCollect); //new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setCollectAndroidID(bool shouldCollect); #endif //old AppsFlyer.setImeiData(string imeiData); //new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setImeiData(string imeiData); #endif //old AppsFlyer.updateServerUninstallToken(string token); //new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.updateServerUninstallToken(string token); #endif //old AppsFlyer.setAndroidIdData(string androidIdData); //new #if UNITY_ANDROID && !UNITY_EDITOR AppAppsFlyersFlyerAndroid.setAndroidIdData("androidId"); #endif //old AppsFlyer.setPreinstallAttribution(string mediaSource, string campaign, string siteId); //new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.setPreinstallAttribution("mediaSource", "campaign", "siteId"); #endif //old AppsFlyer.handlePushNotification(Dictionary payload); //new #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.handlePushNotifications(); #endif ``` ## Validate Receipt ```c# // android old api AppsFlyer.validate-receipt(string publicKey, string purchaseData, string signature, string price, string currency, Dictionary extraParams); // iOS old api AppsFlyer.validate-receipt(string productIdentifier, string price, string currency, string transactionId, Dictionary additionalParametes); AppsFlyer.createValidateInAppListener(string aObject, string callbackMethod, string callbackFailedMethod); // ⚠️ Deprecated: Use V2 version with AFPurchaseDetailsAndroid instead #if UNITY_ANDROID && !UNITY_EDITOR AppsFlyer.validateAndSendInAppPurchase( "publicKey", "signature", "purchaseData", "price", "currency", null, this); #endif // ⚠️ Deprecated: Use V2 version with AFSDKPurchaseDetailsIOS instead #if UNITY_IOS && !UNITY_EDITOR AppsFlyer.validateAndSendInAppPurchase( "productIdentifier", "price", "currency", "tranactionId", null, this); #endif ``` ## Deprecated ```c# //@Deprecated AppsFlyer.enableUninstallTracking(string senderId); AppsFlyer.getHost(); AppsFlyer.loadConversionData(string callbackObject, string callbackMethod, string callbackFailedMethod); AppsFlyer.setGCMProjectNumber(string googleGCMNumber); AppsFlyer.setShouldCollectDeviceName(bool shouldCollectDeviceName); ``` ================================================ FILE: docs/PushNotifications.md ================================================ --- title: Push Notifications category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 9 hidden: false --- ## Unity push notifications using OneLink & Firebase Recommended This is the recommended method for implementing push notification measurement in the Unity Appsflyer SDK. **To integrate AppsFlyer with Android push notifications:** 1. In your `AppsFlyerObjectScript.cs`, call `addPushNotificationDeepLinkPath` **before** calling `start`: ```csharp AppsFlyer.addPushNotificationDeepLinkPath("af_push_link"); ``` In the example above, the SDK is configured to look for the `af_push_link` key in the first level of the push notification payload. When calling `addPushNotificationDeepLinkPath` the SDK verifies that: - The required key exists in the payload. - The key contains a valid OneLink URL. > 📘 Note > > `addPushNotificationDeepLinkPath` accepts an array of strings too, to allow you to extract the relevant key from nested JSON structures. For more information, see [`addPushNotificationDeepLinkPath`](https://dev.appsflyer.com/hc/docs/api#addpushnotificationdeeplinkpath). 2. Create a new Firebase Unity app and follow the [Firebase guide](https://firebase.google.com/docs/unity/setup) (Import the SDK package and the GoogleService files to Unity) 3. Create FirebaseManager empty object and add `FirebaseManager.cs` to it: ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; using Firebase.Extensions; using System; using System.Threading.Tasks; using AppsFlyerSDK; public class FirebaseManager : MonoBehaviour { private Vector2 scrollViewVector = Vector2.zero; private string logText = ""; const int kMaxLogSize = 16382; Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther; protected bool isFirebaseInitialized = false; private string topic = "TestTopic"; // Log the result of the specified task, returning true if the task // completed successfully, false otherwise. protected bool LogTaskCompletion(Task task, string operation) { bool complete = false; if (task.IsCanceled) { DebugLog(operation + " canceled."); } else if (task.IsFaulted) { DebugLog(operation + " encounted an error."); foreach (Exception exception in task.Exception.Flatten().InnerExceptions) { string errorCode = ""; Firebase.FirebaseException firebaseEx = exception as Firebase.FirebaseException; if (firebaseEx != null) { errorCode = String.Format("Error.{0}: ", ((Firebase.Messaging.Error)firebaseEx.ErrorCode).ToString()); } DebugLog(errorCode + exception.ToString()); } } else if (task.IsCompleted) { DebugLog(operation + " completed"); complete = true; } return complete; } // When the app starts, check to make sure that we have // the required dependencies to use Firebase, and if not, // add them if possible. protected virtual void Start() { Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => { dependencyStatus = task.Result; if (dependencyStatus == Firebase.DependencyStatus.Available) { InitializeFirebase(); } else { Debug.LogError( "Could not resolve all Firebase dependencies: " + dependencyStatus); } }); } // Setup message event handlers. void InitializeFirebase() { Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived; Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived; Firebase.Messaging.FirebaseMessaging.SubscribeAsync(topic).ContinueWithOnMainThread(task => { LogTaskCompletion(task, "SubscribeAsync"); }); DebugLog("Firebase Messaging Initialized"); // This will display the prompt to request permission to receive // notifications if the prompt has not already been displayed before. (If // the user already responded to the prompt, thier decision is cached by // the OS and can be changed in the OS settings). Firebase.Messaging.FirebaseMessaging.RequestPermissionAsync().ContinueWithOnMainThread( task => { LogTaskCompletion(task, "RequestPermissionAsync"); } ); isFirebaseInitialized = true; } public virtual void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e) { DebugLog("Received a new message"); var notification = e.Message.Notification; if (notification != null) { DebugLog("title: " + notification.Title); DebugLog("body: " + notification.Body); var android = notification.Android; if (android != null) { DebugLog("android channel_id: " + android.ChannelId); } } if (e.Message.From.Length > 0) DebugLog("from: " + e.Message.From); if (e.Message.Link != null) { DebugLog("link: " + e.Message.Link.ToString()); } if (e.Message.Data.Count > 0) { DebugLog("data:"); foreach (System.Collections.Generic.KeyValuePair iter in e.Message.Data) { DebugLog(" " + iter.Key + ": " + iter.Value); } } #if UNITY_IOS && !UNITY_EDITOR DebugLog("DidReceivedDeepLink: true"); appsFlyerObj.DidReceivedDeepLink = true; var dataDict = new Dictionary(e.Message.Data); AppsFlyeriOS.handlePushNotification(dataDict); #endif } public virtual void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token) { DebugLog("Received Registration Token: " + token.Token); } public void ToggleTokenOnInit() { bool newValue = !Firebase.Messaging.FirebaseMessaging.TokenRegistrationOnInitEnabled; Firebase.Messaging.FirebaseMessaging.TokenRegistrationOnInitEnabled = newValue; DebugLog("Set TokenRegistrationOnInitEnabled to " + newValue); } // Exit if escape (or back, on mobile) is pressed. protected virtual void Update() { if (Input.GetKeyDown(KeyCode.Escape)) { Application.Quit(); } } // End our messaging session when the program exits. public void OnDestroy() { Firebase.Messaging.FirebaseMessaging.MessageReceived -= OnMessageReceived; Firebase.Messaging.FirebaseMessaging.TokenReceived -= OnTokenReceived; } // Output text to the debug log text field, as well as the console. public void DebugLog(string s) { print(s); logText += s + "\n"; while (logText.Length > kMaxLogSize) { int index = logText.IndexOf("\n"); logText = logText.Substring(index + 1); } scrollViewVector.y = int.MaxValue; } } ``` > 📘 Note > > In the `OnMessageReceived` function, for iOS specifically, we are calling the `AppsFlyeriOS.handlePushNotification(Dictionary pushPayload)` method ([read more](https://dev.appsflyer.com/hc/docs/api#addpushnotificationdeeplinkpath)) in order to trigger the AppsFlyerSDK method that is getting overridden due to Firebase's swizzling. ## iOS 1. Add an [APN Authentication key](https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_authentication_key) to your Firebase Unity iOS app ![Firebase - project setting - cloud messeging](https://files.readme.io/a3e7231-Screenshot_2023-05-30_at_18.35.30.png) 2. After building your project, open the XCode project and add the following capabilities: - Push Notifications - Background Modes -> Remote Notifications - Associated Domains (for UDL) - [read more](https://developer.apple.com/documentation/xcode/supporting-associated-domains) 3. If you are getting errors when building the app due to bitcode, disable bitcode ## Android 1. In the AndroidManifest: Add the following service: ```xml ``` 2. Create a Keystore and a key for your app and generate the SHA1 fingerprint ([and the SHA256 fingerprint for Android App Links](https://dev.appsflyer.com/hc/docs/dl_android_init_setup#procedures-for-android-app-links)) - SHA1 for Firebase ![Firebase - app settings - Android app - adding SHA1 fingerprint](https://files.readme.io/0bfbfe6-Screenshot_2023-05-30_at_18.39.06.png) ================================================ FILE: docs/Testing.md ================================================ --- title: Test Integration category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 2 hidden: false --- You are can test your integration for the following OS: - [Testing for iOS/Android](#testing-for-iosandroid) - [Testing for Windows](#testing-for-windows) ## Testing for iOS/Android In order to test the plugin, you need to build an iOS/Android app. Then you can follow these guides: - [Marketers](https://support.appsflyer.com/hc/en-us/articles/360001559405-Test-mobile-SDK-integration-with-the-app#introduction). - [Android](https://dev.appsflyer.com/hc/docs/testing-android) - [iOS](https://dev.appsflyer.com/hc/docs/testing-ios) To enable the debug logs, set the following API to true: ```c# AppsFlyer.setIsDebug(true); ``` --- ## Testing for Windows In order to test the plugin, you need to build your UWP app. To enable the debug logs, please uncomment the following line in [AppsFlyerWindows.cs](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/d0f1c05d17dc4e400609ca880f5079c31fdee73e/Assets/AppsFlyer/Windows/AppsFlyerWindows.cs#L1) file. ```c# #define AFSDK_WIN_DEBUG ``` After running the app, you will be able to find the logs in `%USERPROFILE%\AppData\Local\Packages\TempState\UnityPlayer.log` ================================================ FILE: docs/Troubleshooting.md ================================================ --- title: Troubleshooting category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 10 hidden: false --- # iOS Swizzling * AppsFlyer Unity Plugin uses the [iOS life cycle](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle) events for the SDK to work. * The plugins uses [UnityAppController](https://docs.unity3d.com/Manual/UnityasaLibrary-iOS.html) for the lifecycle events to be invoked. * Sometimes other plugins (Firebase, Facebook, ect) use the same UnityAppController, which creates conflicts in the lifecycle events. * These events include didBecomeActive, didEnterBackground, didReceiveRemoteNotification, continueUserActivity and openURL. * When a conflict occurs these methods may not be invoked. * The solution provided by the AppsFlyer Unity Plugin is [Swizzling](https://medium.com/rocknnull/ios-to-swizzle-or-not-to-swizzle-f8b0ed4a1ce6). * Starting from `v6.0.7` there is an option to enable swizzling automatically. To enable Swizzling, you have 3 options: * For versions up to `6.5.3` - [Using info .plist](#using-info-plist) - [Using a c# Script](#using-a-c-script) * From version `6.5.3` - [Using macroprocessor starting v6.5.3](#using-macroprocessor) ## Using info .plist * To enable swizzling, in the info.plist file, a boolean K/V called `AppsFlyerShouldSwizzle` should be set to 1 (true). * This will automatically enable swizzling and solve conflicts with other plugins. * Validate that the code in the [AppsFlyer+AppController](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/Assets/AppsFlyer/Plugins/iOS/AppsFlyer%2BAppController.m) is called on the native side. * Comment out `IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController)` in AppsFlyerAppController.mm. --- ## Using a c# Script 1. Create a new c# script. (we called ours AFUpdatePlist.cs) 2. Place the script in a editor folder (Assets > Editor > AFUpdatePlist.cs) 3. The code in the script should look like this: ```c# using System.IO; using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; public class MyBuildPostprocessor { [PostProcessBuildAttribute] public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) { if (target == BuildTarget.iOS) { string plistPath = pathToBuiltProject + "/Info.plist"; PlistDocument plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict rootDict = plist.root; rootDict.SetBoolean("AppsFlyerShouldSwizzle", true); File.WriteAllText(plistPath, plist.WriteToString()); Debug.Log("Info.plist updated with AppsFlyerShouldSwizzle"); } } } ``` 4. Validate that the code in the [AppsFlyer+AppController](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/Assets/AppsFlyer/Plugins/iOS/AppsFlyer%2BAppController.m) is called on the native side. 5. Comment out `IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController)` in AppsFlyerAppController.mm. --- ## Using macroprocessor * Add the [preprocessor macro](https://stackoverflow.com/a/26928784) flag `​AFSDK_SHOULD_SWIZZLE=1` to the build settings of the project. ![alt text](https://user-images.githubusercontent.com/61788924/199495968-7aa911ed-27c4-4e5b-a496-3771d0405fd4.jpeg) * Validate that the code in the [AppsFlyer+AppController](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin/blob/master/Assets/AppsFlyer/Plugins/iOS/AppsFlyer%2BAppController.m) is called on the native side. --- # Updating the info.plist In this example, we will update the info.plist to send SKAN postbacks to AppsFlyer, but the script can be adjusted to update any key in the info.plist 1. Create a new c# script. (we called ours AFUpdatePlist.cs) 2. Place the script in a editor folder (Assets > Editor > AFUpdatePlist.cs) 3. The code in the script should look like this: ```c# using System.IO; using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; public class MyBuildPostprocessor { [PostProcessBuildAttribute] public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) { if (target == BuildTarget.iOS) { string plistPath = pathToBuiltProject + "/Info.plist"; PlistDocument plist = new PlistDocument(); plist.ReadFromString(File.ReadAllText(plistPath)); PlistElementDict rootDict = plist.root; rootDict.SetString("NSAdvertisingAttributionReportEndpoint", "https://appsflyer-skadnetwork.com/"); /*** To add more keys : ** rootDict.SetString("", ""); ***/ File.WriteAllText(plistPath, plist.WriteToString()); Debug.Log("Info.plist updated with NSAdvertisingAttributionReportEndpoint"); } } } ``` ================================================ FILE: docs/UnifiedDeepLink.md ================================================ --- title: Unified Deep Linking (UDL) category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 7 hidden: false --- > 📘 **UDL privacy protection** > > For new users, the UDL method only returns parameters relevant to deferred deep linking: `deep_link_value` and `deep_link_sub1` to `deep_link_sub10`. If you try to get any other parameters (`media_source`, `campaign`, `af_sub1-5`, etc.), they return `null`. # UDL flow 1. The SDK is triggered by: - **Deferred Deep Linking** - using a dedicated API - **Direct Deep Linking** - triggered by the OS via Android App Link, iOS Universal Links or URI scheme. 2. The SDK triggers the `OnDeepLink` method, and passes the deep link result object to the user. 3. The `OnDeepLink` method uses the deep link result object that includes the `deep_link_value` and other parameters to create the personalized experience for the users, which is the main goal of OneLink. > Check out the Unified Deep Linking docs for [Android](https://dev.appsflyer.com/docs/android-unified-deep-linking) and [iOS](https://dev.appsflyer.com/docs/ios-unified-deep-linking). # Considerations * Requires AppsFlyer Android SDK V6.1.3 or later. * Does not support SRN campaigns. * For new users, the UDL method only returns parameters relevant to deferred deep linking: `deep_link_value` and `deep_link_sub1-10`. If you try to get any other parameters (media_source, campaign, af_sub1-5, etc.), they return `null`. * `onAppOpenAttribution` will not be called. All code should migrate to `OnDeepLink`. * `OnDeepLink` must be called **after** `initSDK`. * `AppsFlyer.cs` **must** be attached to the game object. # Implementation 1. Attach `AppsFlyer.cs` script to the game object with the AppsFlyer init code. (AppsFlyerObject) 2. Call initSDK with the `this` parameter in order for the `OnDeepLinkReceived` callback to be invoked: ```c# AppsFlyer.initSDK("devkey", "appID", this); ``` 3. Assign `OnDeepLink` to `AppsFlyer.OnDeepLinkReceived` in `Start()` ```c# AppsFlyer.OnDeepLinkReceived += OnDeepLink; ``` 4. After `initSDK()` implement `OnDeepLink`. ## Example ```c# using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour { void Start() { AppsFlyer.initSDK("devkey", "appID", this); AppsFlyer.OnDeepLinkReceived += OnDeepLink; AppsFlyer.startSDK(); } void OnDeepLink(object sender, EventArgs args) { var deepLinkEventArgs = args as DeepLinkEventsArgs; switch (deepLinkEventArgs.status) { case DeepLinkStatus.FOUND: if (deepLinkEventArgs.isDeferred()) { AppsFlyer.AFLog("OnDeepLink", "This is a deferred deep link"); } else { AppsFlyer.AFLog("OnDeepLink", "This is a direct deep link"); } // deepLinkParamsDictionary contains all the deep link parameters as keys Dictionary deepLinkParamsDictionary = null; #if UNITY_IOS && !UNITY_EDITOR if (deepLinkEventArgs.deepLink.ContainsKey("click_event") && deepLinkEventArgs.deepLink["click_event"] != null) { deepLinkParamsDictionary = deepLinkEventArgs.deepLink["click_event"] as Dictionary; } #elif UNITY_ANDROID && !UNITY_EDITOR deepLinkParamsDictionary = deepLinkEventArgs.deepLink; #endif break; case DeepLinkStatus.NOT_FOUND: AppsFlyer.AFLog("OnDeepLink", "Deep link not found"); break; default: AppsFlyer.AFLog("OnDeepLink", "Deep link error"); break; } } } ``` ================================================ FILE: docs/UninstallMeasurement.md ================================================ --- title: Uninstall Measurement category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 5 hidden: false --- - [iOS](#ios) - [Android](#android) ## iOS AppsFlyer enables you to track app uninstalls. To handle notifications it requires to modify your `AppDelegate.m`. Use [didRegisterForRemoteNotificationsWithDeviceToken](https://developer.apple.com/reference/uikit/uiapplicationdelegate) to register to the uninstall feature. UnityEngine.iOS.NotificationServices is now deprecated. Please use the "Mobile Notifications" package instead. It is available in the Unity package manager. *Example:* ```c# using AppsFlyerSDK; using Unity.Notifications.iOS; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData { void Start() { AppsFlyer.initSDK("devKey", "appID", this); AppsFlyer.startSDK(); #if UNITY_IOS StartCoroutine(RequestAuthorization()); Screen.orientation = ScreenOrientation.Portrait; #endif } #if UNITY_IOS IEnumerator RequestAuthorization() { using (var req = new AuthorizationRequest(AuthorizationOption.Alert | AuthorizationOption.Badge, true)) { while (!req.IsFinished) { yield return null; } if (req.Granted && req.DeviceToken != "") { byte[] tokenBytes = ConvertHexStringToByteArray(req.DeviceToken); AppsFlyer.registerUninstall(tokenBytes); } } } private byte[] ConvertHexStringToByteArray(string hexString) { byte[] data = new byte[hexString.Length / 2]; for (int index = 0; index < data.Length; index++) { string byteValue = hexString.Substring(index * 2, 2); data[index] = System.Convert.ToByte(byteValue, 16); } return data; } #endif } ``` Read more about Uninstall register: [Appsflyer SDK support site](https://support.appsflyer.com/hc/en-us/articles/207032066-AppsFlyer-SDK-Integration-iOS) --- ## Android 1. Download the Unity Firebase SDK from: https://firebase.google.com/docs/unity/setup. 2. Import FirebaseMessaging.unitypackage into the project. 3. Import google-services.json into the project (obtained in the Firebase console) **Note** Manifest receivers should be automatically added by the Unity Firebase SDK. 4. In the Unity class handling the AppsFlyer code, add the following: ```c# using Firebase.Messaging; using Firebase.Unity; ``` 5. Add to the `Start()` method: ```c# Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived; ``` 6. Add the following method: ```c# public void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token) { #if UNITY_ANDROID AppsFlyer.updateServerUninstallToken(token.Token); #endif } ``` Read more about Android Uninstall Tracking: [Appsflyer SDK support site](https://support.appsflyer.com/hc/en-us/articles/208004986-Android-Uninstall-Tracking) --- ================================================ FILE: docs/UserInvite.md ================================================ --- title: User Invite category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 8 hidden: false --- # User invite attribution AppsFlyer allows you to attribute and record installs originating from user invites within your app. Allowing your existing users to invite their friends and contacts as new users to your app can be a key growth factor for your app. More information can be found [here](https://dev.appsflyer.com/hc/docs/dl_user_invite). Example: ```c# public class AppsFlyerObjectScript : MonoBehaviour , IAppsFlyerConversionData, IAppsFlyerUserInvite { void Start() { //... AppsFlyer.initSDK("devkey", "appID"); AppsFlyer.setAppInviteOneLinkID("XXXX"); //set up the one link ID for the user invite AppsFlyer.startSDK(); } //... public void generateAppsFlyerLink() { Dictionary parameters = new Dictionary(); parameters.Add("channel", "some_channel"); parameters.Add("campaign", "some_campaign"); parameters.Add("additional_param1", "some_param1"); parameters.Add("additional_param2", "some_param2"); // other params //parameters.Add("referrerName", "some_referrerName"); //parameters.Add("referrerImageUrl", "some_referrerImageUrl"); //parameters.Add("customerID", "some_customerID"); //parameters.Add("baseDeepLink", "some_baseDeepLink"); //parameters.Add("brandDomain", "some_brandDomain"); AppsFlyer.generateUserInviteLink(parameters, this); } ... public void onInviteLinkGenerated(string link) { AppsFlyer.AFLog("onInviteLinkGenerated", link); } public void onInviteLinkGeneratedFailure(string error) { AppsFlyer.AFLog("onInviteLinkGeneratedFailure", error); } public void onOpenStoreLinkGenerated(string link) { AppsFlyer.AFLog("onOpenStoreLinkGenerated", link); } } ``` ================================================ FILE: docs/ad-revenue-unity.md ================================================ --- title: "Ad revenue" slug: "ad-revenue-unity" category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd excerpt: "Impression-level ad revenue reporting by SDK" hidden: false order: 12 --- The app sends impression revenue data to the SDK which then sends it to AppsFlyer. The revenue data is collected and processed in AppsFlyer, and the revenue is attributed to the original UA source. To learn more about ad revenue see [here](https://support.appsflyer.com/hc/en-us/articles/217490046#connect-to-ad-revenue-integrated-partners). There are two ways for the SDK to generate an ad revenue event, depending on your SDK version. Use the correct method for your SDK version: - [For SDK 6.15.0 and above](#log-ad-revenue-for-sdk-6150-and-above). Uses the ad revenue SDK API. - [For SDK 6.14.2 and below](#legacy-log-ad-revenue-for-sdk-6142-and-below). Uses the ad revenue SDK connector. ## Log ad revenue (for SDK 6.15.0 and above) When an impression with revenue occurs, invoke the [`logAdRevenue`](doc:api#logadrevenue) method with the revenue details of the impression. **To implement the method:** 1. Create an instance of `AFAdRevenueData` with the revenue details of the impression to be logged. Version 6.15.0 of the SDK removes the need for using a connector for sending Ad Revenue data to AppsFlyer. 2. If you want to add additional details to the ad revenue event, populate a map with key-value pairs. 3. Invoke the  `logAdRevenue` method with the following arguments: - The `AFAdRevenueData` object you created in step 1. - The `Map` instance with the additional details you created in step 2. ### Code Example ```c# Dictionary additionalParams = new Dictionary(); additionalParams.Add(AdRevenueScheme.COUNTRY, "USA"); additionalParams.Add(AdRevenueScheme.AD_UNIT, "89b8c0159a50ebd1"); additionalParams.Add(AdRevenueScheme.AD_TYPE, "Banner"); additionalParams.Add(AdRevenueScheme.PLACEMENT, "place"); var logRevenue = new AFAdRevenueData("monetizationNetworkEx", MediationNetwork.GoogleAdMob, "USD", 0.99); AppsFlyer.logAdRevenue(logRevenue, additionalParams); ``` > 📘 Note > The AdMob iLTV SDK reports impression revenue in micro-units. To display the correct ad revenue amount in USD in AppsFlyer, divide the amount extracted from the iLTV event handler by 1 million before sending it to AppsFlyer. ## [LEGACY] Log ad revenue (for SDK 6.14.2 and below) For SDK v6.14.2 and below - the AdRevenue Connector should be used along side the AppsFlyer SDK to send Ad Revenue data to AppsFlyer. ### Using Unity Package 1. Clone or download [the Ad revenue connector](https://github.com/AppsFlyerSDK/appsflyer-unity-adrevenue-generic-connector/tree/main) repository. 2. Import the Adrevenue Unity package into your Unity project (To learn how to import to Unity refer to the [Unity documentation](https://docs.unity3d.com/Manual/AssetPackages.html)). 1. In Unity, go to **Assets** > **Import Package** > **Custom Package** 2. From the repository root select the `appsflyer-unity-adrevenue-plugin-x.x.x.unitypackage` file. ### Using Unity Package Manager 1. Add the dependency in your `manifest.json` file: ``` "appsflyer-unity-adrevenue-generic-connector": "https://github.com/AppsFlyerSDK/appsflyer-unity-adrevenue-generic-connector.git#upm" ``` 2. If you haven't already done so, download the [External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver) to be able to resolve our Android / iOS dependencies. **Note:** To choose a specific version and not the latest, you can replace the `upm` with the specific version tag, `v6.9.4-upm` for example. ### Initialize the connector Make sure to initialize the AppsFlyer SDK before initializing the connector. ```java using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour { void Start() { AppsFlyerAdRevenue.start(); /* AppsFlyerAdRevenue.setIsDebug(true); */ } } ``` ### Ad revenue connector API #### `start` `public static void start()` Start sending AdRevenue data to AppsFlyer. _Example:_ ```java using AppsFlyerSDK; void Start() { AppsFlyerAdRevenue.start(); } ``` #### `setIsDebug` `public static void setIsDebug(bool isDebug)` Set to true to view debug logs. (development only!) | parameter | type | description | | --------- | ---- | ------------------------------- | | isDebug | bool | set to true in development only | _Example:_ ```java AppsFlyerAdRevenue.setIsDebug(true); ``` **Note:** This API will only set the debug logs for iOS. For Android the debug logs are controlled by the native SDK. To turn on the debug logs on Android call `AppsFlyer.setIsDebug(true);` #### `logAdRevenue` `public static void logAdRevenue(string monetizationNetwork, AppsFlyerAdRevenueMediationNetworkType mediationNetwork, double eventRevenue, string revenueCurrency, Dictionary additionalParameters)` Send ad revenue data from the impression payload to AppsFlyer regardless of the mediation network you use. | parameter | type | description | | -------------------- | -------------------------------------- | -------------------------------- | | monetizationNetwork | string | monetization network | | mediationNetwork | AppsFlyerAdRevenueMediationNetworkType | Enum for mediaton network type | | eventRevenue | string | event revenue | | revenueCurrency | string | revenue currency | | additionalParameters | Dictionary | Any custom additional parameters | | | | | _Example:_ ```java Dictionary additionalParams = new Dictionary(); additionalParams.Add(AFAdRevenueEvent.COUNTRY, "US"); additionalParams.Add(AFAdRevenueEvent.AD_UNIT, "89b8c0159a50ebd1"); additionalParams.Add(AFAdRevenueEvent.AD_TYPE, "Banner"); additionalParams.Add(AFAdRevenueEvent.PLACEMENT, "place"); additionalParams.Add("custom", "foo"); additionalParams.Add("custom_2", "bar"); additionalParams.Add("af_quantity", "1"); AppsFlyerAdRevenue.logAdRevenue("facebook", AppsFlyerAdRevenueMediationNetworkType.AppsFlyerAdRevenueMediationNetworkTypeGoogleAdMob, 0.026, "USD", additionalParams); ``` ================================================ FILE: docs/conversion-data-unity.md ================================================ --- title: Conversion data category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd order: 4 hidden: false --- In this guide, you will learn how to get conversion data using [`IAppsFlyerConversionData`](https://dev.appsflyer.com/hc/docs/api#iappsflyerconversiondata), as well as examples for using the conversion data. Learn more about [what is conversion data](https://dev.appsflyer.com/hc/docs/conversion-data). ## Obtain AppsFlyer conversion data 1. Implement the [`IAppsFlyerConversionData`](https://dev.appsflyer.com/hc/docs/api#iappsflyerconversiondata) class. 2. Call the [`initSDK`](https://dev.appsflyer.com/hc/docs/api#initsdk) method with `this` as the last parameter. 3. Use the [`onConversionDataSuccess`](https://dev.appsflyer.com/hc/docs/api#onconversiondatasuccess) method to redirect the user. ## Example ```c# using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour , IAppsFlyerConversionData{ void Start() { /* AppsFlyer.setDebugLog(true); */ AppsFlyer.initSDK("devkey", "appID", this); AppsFlyer.startSDK(); } public void onConversionDataSuccess(string conversionData) { AppsFlyer.AFLog("onConversionDataSuccess", conversionData); Dictionary conversionDataDictionary = AppsFlyer.CallbackStringToDictionary(conversionData); } public void onConversionDataFail(string error) { AppsFlyer.AFLog("onConversionDataFail", error); } public void onAppOpenAttribution(string attributionData) { AppsFlyer.AFLog("onAppOpenAttribution: This method was replaced by UDL. This is a fake call.", attributionData); } public void onAppOpenAttributionFailure(string error) { AppsFlyer.AFLog("onAppOpenAttributionFailure: This method was replaced by UDL. This is a fake call.", error); } } ``` ================================================ FILE: docs/purchase-connector.md ================================================ --- title: "Purchase connector" slug: "purchase-connector-unity" category: 5f9705393c689a065c409b23 parentDoc: 694bc4503a665449be928691 excerpt: "Used to validate and report in-app purchase and subscription revenue events" hidden: false order: 2 --- ## Overview The AppsFlyer ROI360 purchase connector is used to validate and report in-app purchase and subscription revenue events. It's part of the ROI360 in-app purchase and subscription revenue measurement solution. - Using the purchase connector requires an ROI360 subscription. - If you use this in-app purchase and subscription revenue measurement solution, you shouldn't send [in-app purchase events](https://dev.appsflyer.com/hc/docs/inappevents) with revenue or execute [`validateAndLogInAppPurchase`](https://dev.appsflyer.com/hc/docs/validate-and-log-purchase-ios), as doing so results in duplicate revenue being reported. - Before implementing the purchase connector, the ROI360 in-app purchase and subscription revenue measurement needs to be integrated with Google Play and the App Store. [See instructions (steps 1 and 2)](https://support.appsflyer.com/hc/en-us/articles/7459048170769) ## Prerequisites ### iOS Requirements - StoreKit SDK v1 or v2 (StoreKit 2 requires iOS 15+) - iOS version 9 and higher - Unity AppsFlyer plugin **6.17.1** and higher ### Android Requirements - Google Play Billing library version **7.x.x** and **8.x.x** - Unity AppsFlyer plugin **6.17.1** and higher ### General Requirements - Unity version **2020.3** and higher - ROI360 subscription ## 📌 Important: Two Versions of Unity Plugin v6.17.7 We have released **two** versions of the AppsFlyer Unity plugin to support teams at different stages of migrating to **Google Play Billing Library v8.0.0**. ### Option A — `v6.17.7` (Billing Library v8) - **What’s included:** Support for **Google Play Billing Library 8.0.0** on Android (Android Purchase Connector version - 2.2.0). - **Impact:** This version may introduce **breaking changes** for apps that have **not yet migrated** to the Billing v8 APIs. - **Unity IAP requirement:** If you choose this option, update **Unity IAP (`com.unity.purchasing`) to version 5.0.0 or newer** (we recommend the latest 5.x). Unity IAP 4.x does **not** include Billing v8. ### Option B — `v6.17.72` (Billing Library v7) - **Purpose:** For developers **not ready** to adopt Billing v8. - **Bundled SDKs:** **iOS SDK 6.17.7** and **Android SDK 6.17.3**. - **Impact:** Lets you update the AppsFlyer SDKs without changing your existing (pre‑v8) billing integration. ## Installation ### Method 1: Integrated Approach (v6.17.1+) - **Recommended** **Starting with version 6.17.1, the Purchase Connector is integrated directly into the main AppsFlyer Unity plugin.** You no longer need to download or import a separate package. 1. Download the latest [AppsFlyer Unity plugin](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin) (v6.17.1 or higher) 2. [Import](https://docs.unity3d.com/Manual/AssetPackages.html) the `appsflyer-unity-plugin-x.x.x.unitypackage` into your Unity project - Go to **Assets** → **Import Package** → **Custom Package** - Select the `appsflyer-unity-plugin-x.x.x.unitypackage` The Purchase Connector functionality is now included automatically - no additional imports required! If you previously used the standalone Purchase Connector, simply remove any references to using AppsFlyerConnector; from your codebase, as its functionality is now included in the main plugin. ### Method 2: Separate Repository Approach (Pre-v6.17.1) **This approach is only relevant for versions prior to 6.17.1.** Starting with version 6.17.1, the Purchase Connector is integrated into the main AppsFlyer Unity plugin and this separate repository approach is no longer needed. If you are using a version older than 6.17.1: 1. Download the [AppsFlyer Unity plugin](https://github.com/AppsFlyerSDK/appsflyer-unity-plugin) 2. Download the [Purchase Connector repository](https://github.com/AppsFlyerSDK/appsflyer-unity-purchase-connector) 3. Import both packages into your Unity project: - First import `appsflyer-unity-plugin-x.x.x.unitypackage` - Then import `appsflyer-unity-purchase-connector-x.x.x.unitypackage` **Note:** When using the separate repository approach, make sure the Purchase Connector version is compatible with your AppsFlyer Unity plugin version. **Recommendation:** Consider upgrading to version 6.17.1+ to use the integrated approach for simplified setup and access to the latest features. ## ProGuard Rules _Android Only_ - If you are using ProGuard, add the following keep rules to your `proguard-rules.pro` file: ```groovy -keep class com.appsflyer.** { *; } -keep class kotlin.jvm.internal.Intrinsics{ *; } -keep class kotlin.collections.**{ *; } -keep class kotlin.Result$Companion { *; } ``` ## Strict Mode Support The Purchase Connector supports Strict Mode, which completely removes IDFA collection functionality and AdSupport framework dependencies. Use Strict Mode when developing apps for kids or when IDFA collection is not desired. Make sure to use the strict mode AppsFlyer Unity plugin along with the Purchase Connector's strict mode functionality. ## Choosing Your Implementation Method ### When to Use Integrated Approach (v6.17.1+) ✅ **Recommended for:** - New projects starting with v6.17.1+ - Existing projects that can upgrade to v6.17.1+ - Simplified setup and maintenance - Access to latest features like StoreKit 2 support ## Implementation Guide The implementation differs slightly depending on which installation method you chose: ### Required Interfaces Your MonoBehaviour class must implement the following interfaces: **For Integrated Approach (v6.17.1+):** ```csharp using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, // For conversion data callbacks IAppsFlyerPurchaseValidation, // For purchase validation callbacks IAppsFlyerPurchaseRevenueDataSource, // For StoreKit 1 additional parameters IAppsFlyerPurchaseRevenueDataSourceStoreKit2 // For StoreKit 2 additional parameters { // Implementation goes here } ``` **For Separate Repository Approach:** ```csharp using AppsFlyerSDK; using AppsFlyerConnector; // Additional namespace for separate repository public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, // For conversion data callbacks IAppsFlyerPurchaseValidation, // For purchase validation callbacks IAppsFlyerPurchaseRevenueDataSource, // For additional parameters (iOS) { // Implementation goes here } ``` ### Basic Setup #### Integrated Approach (v6.17.1+) ```csharp using UnityEngine; using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, IAppsFlyerPurchaseValidation, IAppsFlyerPurchaseRevenueDataSource, IAppsFlyerPurchaseRevenueDataSourceStoreKit2 { [Header("AppsFlyer Settings")] public string devKey = "YOUR_DEV_KEY"; public string appID = "YOUR_APP_ID"; public bool isDebug = true; public bool getConversionData = true; void Start() { // 1. Initialize AppsFlyer SDK AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null); AppsFlyer.setIsDebug(isDebug); // 2. Initialize Purchase Connector AppsFlyerPurchaseConnector.init(this, Store.GOOGLE); // 3. Configure Purchase Connector ConfigurePurchaseConnector(); // 4. Build and start observing AppsFlyerPurchaseConnector.build(); AppsFlyerPurchaseConnector.startObservingTransactions(); // 5. Start AppsFlyer SDK AppsFlyer.startSDK(); } private void ConfigurePurchaseConnector() { // Set sandbox mode for testing AppsFlyerPurchaseConnector.setIsSandbox(true); // Configure StoreKit version (iOS only) - SK1 is the default AppsFlyerPurchaseConnector.setStoreKitVersion(StoreKitVersion.SK2); // Enable automatic logging for subscriptions and in-app purchases AppsFlyerPurchaseConnector.setAutoLogPurchaseRevenue( AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions, AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases ); // Enable purchase validation callbacks AppsFlyerPurchaseConnector.setPurchaseRevenueValidationListeners(true); // Set data sources for additional parameters (iOS) - SK1 AppsFlyerPurchaseConnector.setPurchaseRevenueDataSource(this); // Set data sources for additional parameters (iOS) - SK2 AppsFlyerPurchaseConnector.setPurchaseRevenueDataSourceStoreKit2(this); } } ``` #### Legacy (2-repos) Approach ```csharp using UnityEngine; using AppsFlyerSDK; using AppsFlyerConnector; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, IAppsFlyerPurchaseValidation, IAppsFlyerPurchaseRevenueDataSource { [Header("AppsFlyer Settings")] public string devKey = "YOUR_DEV_KEY"; public string appID = "YOUR_APP_ID"; public bool isDebug = true; public bool getConversionData = true; void Start() { // 1. Initialize AppsFlyer SDK AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null); AppsFlyer.setIsDebug(isDebug); // 2. Initialize Purchase Connector (using AppsFlyerConnector namespace) AppsFlyerPurchaseConnector.init(this, AppsFlyerConnector.Store.GOOGLE); // 3. Configure Purchase Connector ConfigurePurchaseConnector(); // 4. Build and start observing AppsFlyerPurchaseConnector.build(); AppsFlyerPurchaseConnector.startObservingTransactions(); // 5. Start AppsFlyer SDK AppsFlyer.startSDK(); } private void ConfigurePurchaseConnector() { // Set sandbox mode for testing AppsFlyerPurchaseConnector.setIsSandbox(true); // Enable automatic logging for subscriptions and in-app purchases AppsFlyerPurchaseConnector.setAutoLogPurchaseRevenue( AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions, AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases ); // Enable purchase validation callbacks AppsFlyerPurchaseConnector.setPurchaseRevenueValidationListeners(true); } } ``` ## Core APIs ### Initialization ```csharp // Initialize Purchase Connector with store type AppsFlyerPurchaseConnector.init(this, Store.GOOGLE); ``` ### Configuration Options #### StoreKit Version (iOS Only) ```csharp // Set StoreKit version - SK1 or SK2 AppsFlyerPurchaseConnector.setStoreKitVersion(StoreKitVersion.SK2); ``` #### Sandbox Mode ```csharp // Enable sandbox mode for testing AppsFlyerPurchaseConnector.setIsSandbox(true); ``` #### Auto-Logging Options ```csharp // Enable automatic logging for specific purchase types AppsFlyerPurchaseConnector.setAutoLogPurchaseRevenue( AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions, AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases ); ``` **Available Options:** - `AppsFlyerAutoLogPurchaseRevenueOptionsDisabled`: Disable automatic logging - `AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions`: Log auto-renewable subscriptions - `AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases`: Log in-app purchases ### Transaction Observation ```csharp // Start observing transactions AppsFlyerPurchaseConnector.startObservingTransactions(); // Stop observing transactions AppsFlyerPurchaseConnector.stopObservingTransactions(); ``` ### Build and Complete Setup ```csharp // Build the Purchase Connector with all configurations AppsFlyerPurchaseConnector.build(); ``` ## Interface Implementations ### IAppsFlyerPurchaseValidation ```csharp public void didReceivePurchaseRevenueValidationInfo(string validationInfo) { AppsFlyer.AFLog("didReceivePurchaseRevenueValidationInfo", validationInfo); Debug.Log("Purchase validation success: " + validationInfo); // Handle different purchase types on Android #if UNITY_ANDROID if (dictionary.ContainsKey("productPurchase") && dictionary["productPurchase"] != null) { // Handle in-app purchase validation result Debug.Log("In-app purchase validated"); } else if (dictionary.ContainsKey("subscriptionPurchase") && dictionary["subscriptionPurchase"] != null) { // Handle subscription validation result Debug.Log("Subscription validated"); } #endif } public void didReceivePurchaseRevenueError(string error) { AppsFlyer.AFLog("didReceivePurchaseRevenueError", error); Debug.LogError("Purchase validation error: " + error); } ``` ### IAppsFlyerPurchaseRevenueDataSource (StoreKit 1) ```csharp public Dictionary PurchaseRevenueAdditionalParametersForProducts( HashSet products, HashSet transactions) { // Add custom parameters to purchase events return new Dictionary { ["custom_param_1"] = "value1", ["custom_param_2"] = "value2", ["user_level"] = 5, ["purchase_source"] = "main_store" }; } ``` ### IAppsFlyerPurchaseRevenueDataSourceStoreKit2 (StoreKit 2) ```csharp public Dictionary PurchaseRevenueAdditionalParametersStoreKit2ForProducts( HashSet products, HashSet transactions) { // Add custom parameters specifically for StoreKit 2 purchases return new Dictionary { ["sk2_custom_param"] = "sk2_value" }; } ``` ## Advanced Features ### StoreKit 2 Consumable Transactions (iOS 15+ to iOS 18+) On iOS 15 and above, consumable in-app purchases are handled via StoreKit 2. - **On iOS 18 and later:** Apple introduced a new Info.plist flag: `SKIncludeConsumableInAppPurchaseHistory`. - If you set `SKIncludeConsumableInAppPurchaseHistory` to `YES` in your Info.plist, automatic collection will happen. - If the flag is not present or is set to `NO`, you must manually log consumable transactions as shown below. - **On iOS 15–18:** Consumable purchases must always be logged manually. ```csharp AppsFlyerPurchaseConnector.logConsumableTransaction(transactionId); //(iOS SK2 only) ``` ### Custom Purchase Validation Callbacks Enable validation callbacks to receive detailed purchase information: ```csharp AppsFlyerPurchaseConnector.setPurchaseRevenueValidationListeners(true); ``` ## Testing in Sandbox ### iOS Testing 1. Set sandbox mode: `AppsFlyerPurchaseConnector.setIsSandbox(true)` 2. Use TestFlight sandbox accounts for testing 3. Test on real devices with Xcode ### Android Testing 1. Set sandbox mode: `AppsFlyerPurchaseConnector.setIsSandbox(true)` 2. Follow [Google Play Billing testing guidelines](https://developer.android.com/google/play/billing/test) 3. Use test accounts and test products > **⚠️ IMPORTANT**: Remove `setIsSandbox(true)` or set it to `false` before releasing to production. Production purchases sent in sandbox mode will not be validated properly! ## Complete Implementation Examples ### Integrated Approach (v6.17.1+) ```csharp using System.Collections.Generic; using UnityEngine; using AppsFlyerSDK; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, IAppsFlyerPurchaseValidation, IAppsFlyerPurchaseRevenueDataSource, IAppsFlyerPurchaseRevenueDataSourceStoreKit2 { [Header("AppsFlyer Configuration")] public string devKey = "YOUR_DEV_KEY"; public string appID = "YOUR_APP_ID"; public string UWPAppID = "YOUR_UWP_APP_ID"; public string macOSAppID = "YOUR_MACOS_APP_ID"; public bool isDebug = true; public bool getConversionData = true; void Start() { // 1. Initialize AppsFlyer SDK #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyer.initSDK(devKey, UWPAppID, getConversionData ? this : null); #elif UNITY_STANDALONE_OSX && !UNITY_EDITOR AppsFlyer.initSDK(devKey, macOSAppID, getConversionData ? this : null); #else AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null); #endif AppsFlyer.setIsDebug(isDebug); // 2. Initialize and configure Purchase Connector AppsFlyerPurchaseConnector.init(this, Store.GOOGLE); AppsFlyerPurchaseConnector.setStoreKitVersion(StoreKitVersion.SK2); AppsFlyerPurchaseConnector.setIsSandbox(true); // Remove for production AppsFlyerPurchaseConnector.setAutoLogPurchaseRevenue( AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions, AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases ); AppsFlyerPurchaseConnector.setPurchaseRevenueValidationListeners(true); AppsFlyerPurchaseConnector.setPurchaseRevenueDataSource(this); AppsFlyerPurchaseConnector.setPurchaseRevenueDataSourceStoreKit2(this); // 3. Build and start AppsFlyerPurchaseConnector.build(); AppsFlyerPurchaseConnector.startObservingTransactions(); // 4. Start AppsFlyer SDK AppsFlyer.startSDK(); Debug.Log("AppsFlyer SDK + Purchase Connector initialized successfully"); } // --- Purchase Revenue Data Sources --- public Dictionary PurchaseRevenueAdditionalParametersForProducts( HashSet products, HashSet transactions) { return new Dictionary { ["storekit_version"] = "1.0", ["additional_param"] = "sk1_value", ["product_count"] = products.Count, ["transaction_count"] = transactions.Count }; } public Dictionary PurchaseRevenueAdditionalParametersStoreKit2ForProducts( HashSet products, HashSet transactions) { return new Dictionary { ["storekit_version"] = "2.0", ["additional_param"] = "sk2_value", ["product_count"] = products.Count, ["transaction_count"] = transactions.Count }; } // --- Purchase Validation Callbacks --- public void didReceivePurchaseRevenueValidationInfo(string validationInfo) { AppsFlyer.AFLog("didReceivePurchaseRevenueValidationInfo", validationInfo); Debug.Log("Purchase validation success: " + validationInfo); // Parse and handle validation info var dict = AFMiniJSON.Json.Deserialize(validationInfo) as Dictionary; #if UNITY_ANDROID if (dict.ContainsKey("productPurchase")) { Debug.Log("Android in-app purchase validated"); } else if (dict.ContainsKey("subscriptionPurchase")) { Debug.Log("Android subscription validated"); } #endif } public void didReceivePurchaseRevenueError(string error) { AppsFlyer.AFLog("didReceivePurchaseRevenueError", error); Debug.LogError("Purchase validation error: " + error); } // --- Conversion Data Callbacks --- public void onConversionDataSuccess(string conversionData) { AppsFlyer.AFLog("onConversionDataSuccess", conversionData); var dict = AppsFlyer.CallbackStringToDictionary(conversionData); // Handle deferred deep linking } public void onConversionDataFail(string error) { AppsFlyer.AFLog("onConversionDataFail", error); } public void onAppOpenAttribution(string attributionData) { AppsFlyer.AFLog("onAppOpenAttribution", attributionData); var dict = AppsFlyer.CallbackStringToDictionary(attributionData); // Handle direct deep linking } public void onAppOpenAttributionFailure(string error) { AppsFlyer.AFLog("onAppOpenAttributionFailure", error); } } ``` ### Legacy Approach ```csharp using System.Collections.Generic; using UnityEngine; using AppsFlyerSDK; using AppsFlyerConnector; public class AppsFlyerObjectScript : MonoBehaviour, IAppsFlyerConversionData, IAppsFlyerPurchaseValidation, IAppsFlyerPurchaseRevenueDataSource, IAppsFlyerPurchaseRevenueDataSourceStoreKit2 { [Header("AppsFlyer Configuration")] public string devKey = "YOUR_DEV_KEY"; public string appID = "YOUR_APP_ID"; public string UWPAppID = "YOUR_UWP_APP_ID"; public string macOSAppID = "YOUR_MACOS_APP_ID"; public bool isDebug = true; public bool getConversionData = true; void Start() { // 1. Initialize AppsFlyer SDK #if UNITY_WSA_10_0 && !UNITY_EDITOR AppsFlyer.initSDK(devKey, UWPAppID, getConversionData ? this : null); #elif UNITY_STANDALONE_OSX && !UNITY_EDITOR AppsFlyer.initSDK(devKey, macOSAppID, getConversionData ? this : null); #else AppsFlyer.initSDK(devKey, appID, getConversionData ? this : null); #endif AppsFlyer.setIsDebug(isDebug); // 2. Initialize and configure Purchase Connector (using separate repository approach) AppsFlyerPurchaseConnector.init(this, AppsFlyerConnector.Store.GOOGLE); AppsFlyerPurchaseConnector.setIsSandbox(true); // Remove for production AppsFlyerPurchaseConnector.setAutoLogPurchaseRevenue( AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsAutoRenewableSubscriptions, AppsFlyerAutoLogPurchaseRevenueOptions.AppsFlyerAutoLogPurchaseRevenueOptionsInAppPurchases ); AppsFlyerPurchaseConnector.setPurchaseRevenueValidationListeners(true); // 3. Build and start AppsFlyerPurchaseConnector.build(); AppsFlyerPurchaseConnector.startObservingTransactions(); // 4. Start AppsFlyer SDK AppsFlyer.startSDK(); Debug.Log("AppsFlyer SDK + Purchase Connector (separate repository) initialized successfully"); } // --- Purchase Revenue Data Sources --- public Dictionary PurchaseRevenueAdditionalParametersForProducts( HashSet products, HashSet transactions) { return new Dictionary { ["implementation_type"] = "separate_repository", ["additional_param"] = "value", ["product_count"] = products.Count, ["transaction_count"] = transactions.Count }; } public Dictionary PurchaseRevenueAdditionalParametersStoreKit2ForProducts( HashSet products, HashSet transactions) { // Note: StoreKit 2 support depends on Purchase Connector version return new Dictionary { ["implementation_type"] = "separate_repository_sk2", ["additional_param"] = "sk2_value", ["product_count"] = products.Count, ["transaction_count"] = transactions.Count }; } // --- Purchase Validation Callbacks --- public void didReceivePurchaseRevenueValidationInfo(string validationInfo) { AppsFlyer.AFLog("didReceivePurchaseRevenueValidationInfo", validationInfo); Debug.Log("Purchase validation success: " + validationInfo); // Parse and handle validation info var dict = AFMiniJSON.Json.Deserialize(validationInfo) as Dictionary; #if UNITY_ANDROID if (dict.ContainsKey("productPurchase")) { Debug.Log("Android in-app purchase validated"); } else if (dict.ContainsKey("subscriptionPurchase")) { Debug.Log("Android subscription validated"); } #endif } public void didReceivePurchaseRevenueError(string error) { AppsFlyer.AFLog("didReceivePurchaseRevenueError", error); Debug.LogError("Purchase validation error: " + error); } // --- Conversion Data Callbacks --- public void onConversionDataSuccess(string conversionData) { AppsFlyer.AFLog("onConversionDataSuccess", conversionData); var dict = AppsFlyer.CallbackStringToDictionary(conversionData); // Handle deferred deep linking } public void onConversionDataFail(string error) { AppsFlyer.AFLog("onConversionDataFail", error); } public void onAppOpenAttribution(string attributionData) { AppsFlyer.AFLog("onAppOpenAttribution", attributionData); var dict = AppsFlyer.CallbackStringToDictionary(attributionData); // Handle direct deep linking } public void onAppOpenAttributionFailure(string error) { AppsFlyer.AFLog("onAppOpenAttributionFailure", error); } } ``` ## Migration Guide ### Migrating from Legacy to Integrated (Recommended) If you're upgrading from the separate repository approach to the integrated approach: 1. **Backup your project** before making changes 2. **Remove old Purchase Connector package**: Delete the old Purchase Connector files from your project 3. **Update AppsFlyer Unity plugin**: Install AppsFlyer Unity plugin v6.17.1 or higher 4. **Update code changes**: ```csharp // OLD (Separate Repository) using AppsFlyerConnector; //Remove // NEW (Integrated) using AppsFlyerSDK; // Only this namespace needed AppsFlyerPurchaseConnector.init(this, Store.GOOGLE); ``` 5. **Test thoroughly**: Verify all Purchase Connector functionality works as expected 6. **Update documentation/comments**: Remove references to separate repository setup ================================================ FILE: docs/purchase-validation-unity.md ================================================ --- title: "Purchase and subscription validation" slug: "purchase-subscription-validation-unity" category: 5f9705393c689a065c409b23 parentDoc: 6370c9e2441a4504d6bca3bd hidden: false order: 13 --- Purchase validation ensures that only real, store-confirmed in-app purchases and subscriptions are measured in AppsFlyer. It improves revenue accuracy, helps prevent reporting errors, and supports better campaign decisions. AppsFlyer offers two products to support purchase validation: - **Receipt validation** – A free, lightweight solution for basic in-app purchase verification. - **ROI360 Store revenue** – A premium, comprehensive solution for full revenue accuracy, including subscription lifecycle coverage and net revenue reporting. For more information, see [Purchase and subscription validation](https://support.appsflyer.com/hc/en-us/articles/42120228484241--WIP-Purchase-and-subscription-validation-Overview). ## SDK Integration Methods AppsFlyer supports two SDK integration methods for sending in-app purchase data to AppsFlyer for validation: ### 1. Manual Integration method – Validate and Log Call Validate and Log (`validateAndSendInAppPurchase`) every time a transaction occurs in the app (such as an in-app purchase, subscription start, or trial start). The method sends the transaction to AppsFlyer, which validates it with the store and generates the relevant in-app event. - Requires an explicit call from the app for every transaction - Suitable for apps that need to capture events not included in the Purchase Connector’s default coverage. With the Validate and log method, developers can explicitly target and send these additional events. To get started see: [`Validate and Log`](https://dev.appsflyer.com/hc/docs/validate-and-log-unity) ### 2. Automated Integration method – Purchase Connector Purchase Connector automatically detects in-app purchases and subscriptions made on the device. Once initialized, it sends the required data to AppsFlyer without additional logging code. - Supported only by ROI360 products and recommended for most apps - Triggers validation automatically and returns the result to the client in real time - The following capabilities cannot be supported through simple customization of the Validate and Log method and therefore require Purchase Connector: - Logging subscription revenue from users who subscribed before the integration was added. - Logging subscription price changes, ensuring revenue reflects updated pricing. To get started see: [Unity purchase SDK connector](https://dev.appsflyer.com/hc/docs/purchase-connector-unity) --- > ⚠️ Important > > To avoid duplicate event logging and inconsistent validation results, it’s recommended to use only one integration method per application. ================================================ FILE: docs/validate-and-log-unity.md ================================================ --- title: "Validate and log purchase" slug: "validate-and-log-unity" category: 5f9705393c689a065c409b23 parentDoc: 694bc4503a665449be928691 excerpt: "Used to validate and report in-app purchase and subscription revenue events" hidden: false order: 1 --- ## Validate and log purchase Follow the instructions according to your operating system. Calling `validateAndSendInAppPurchase` automatically generates an `af_purchase` in-app event, so you don't need to send this event yourself. The validate purchase response is triggered in the `AppsFlyerTrackerCallbacks.cs` class. ```c# // for Android `void validateAndSendInAppPurchase(AFPurchaseDetailsAndroid details, Dictionary additionalParameters, MonoBehaviour gameObject)` // for iOS `void validateAndSendInAppPurchase(AFSDKPurchaseDetailsIOS details, Dictionary extraEventValues, MonoBehaviour gameObject)` ``` ```c# using UnityEngine.Purchasing; using AppsFlyerSDK; public class AppsFlyerObject : MonoBehaviour, IAppsFlyerValidateAndLog { public static string kProductIDConsumable = "com.test.cons"; void Start() { AppsFlyer.initSDK("devKey", "devKey"); AppsFlyer.startSDK(); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { string prodID = args.purchasedProduct.definition.id; string price = args.purchasedProduct.metadata.localizedPrice.ToString(); string currency = args.purchasedProduct.metadata.isoCurrencyCode; string receipt = args.purchasedProduct.receipt; var recptToJSON = (Dictionary)AFMiniJSON.Json.Deserialize(product.receipt); var receiptPayload = (Dictionary)AFMiniJSON.Json.Deserialize((string)recptToJSON["Payload"]); var transactionID = product.transactionID; if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal)) { #if UNITY_IOS if(isSandbox) { AppsFlyeriOS.setUseReceiptValidationSandbox(true); } AFSDKPurchaseDetailsIOS details = AFSDKPurchaseDetailsIOS.Init(prodID, price, currency, transactionID); AppsFlyeriOS.validateAndSendInAppPurchase(details, null, this); #elif UNITY_ANDROID AFPurchaseDetailsAndroid details = new AFPurchaseDetailsAndroid(AFPurchaseType.Subscription, "token", prodID, price, currency); AppsFlyerAndroid.validateAndSendInAppPurchase( details, null, this); #endif } return PurchaseProcessingResult.Complete; } public void onValidateAndLogComplete(string result) { AppsFlyer.AFLog("onValidateAndLogComplete", result); Dictionary validateAndLogDataDictionary = AppsFlyer.CallbackStringToDictionary(result); } public void onValidateAndLogFailure(string error) { AppsFlyer.AFLog("onValidateAndLogFailure", error); Dictionary validateAndLogErrorDictionary = AppsFlyer.CallbackStringToDictionary(error); } } ``` ## Receipt validation [Legacy] Deprecated since V6.17.8 For Receipt Validation, follow the instructions according to your operating system. **Notes** Calling validateReceipt automatically generates an `af_purchase` in-app event, so you don't need to send this event yourself. The validate purchase response is triggered in the `AppsFlyerTrackerCallbacks.cs` class. `void validateAndSendInAppPurchase(string productIdentifier, string price, string currency, string tranactionId, Dictionary additionalParameters, MonoBehaviour gameObject)` ```c# //To get the callbacks //AppsFlyer.createValidateInAppListener ("AppsFlyerTrackerCallbacks", "onInAppBillingSuccess", "onInAppBillingFailure"); AppsFlyer.validateReceipt(string publicKey, string purchaseData, string signature, string price, string currency, Dictionary additionalParametes); ``` ```c# using UnityEngine.Purchasing; using AppsFlyerSDK; public class AppsFlyerObject : MonoBehaviour, IStoreListener, IAppsFlyerValidateReceipt { public static string kProductIDConsumable = "com.test.cons"; void Start() { AppsFlyer.initSDK("devKey", "devKey"); AppsFlyer.startSDK(); } public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args) { string prodID = args.purchasedProduct.definition.id; string price = args.purchasedProduct.metadata.localizedPrice.ToString(); string currency = args.purchasedProduct.metadata.isoCurrencyCode; string receipt = args.purchasedProduct.receipt; var recptToJSON = (Dictionary)AFMiniJSON.Json.Deserialize(product.receipt); var receiptPayload = (Dictionary)AFMiniJSON.Json.Deserialize((string)recptToJSON["Payload"]); var transactionID = product.transactionID; if (String.Equals(args.purchasedProduct.definition.id, kProductIDConsumable, StringComparison.Ordinal)) { #if UNITY_IOS if(isSandbox) { AppsFlyeriOS.setUseReceiptValidationSandbox(true); } AppsFlyeriOS.validateAndSendInAppPurchase(prodID, price, currency, transactionID, null, this); #elif UNITY_ANDROID var purchaseData = (string)receiptPayload["json"]; var signature = (string)receiptPayload["signature"]; AppsFlyerAndroid.validateAndSendInAppPurchase( "", signature, purchaseData, price, currency, null, this); #endif } return PurchaseProcessingResult.Complete; } public void didFinishValidateReceipt(string result) { AppsFlyer.AFLog("didFinishValidateReceipt", result); } public void didFinishValidateReceiptWithError(string error) { AppsFlyer.AFLog("didFinishValidateReceiptWithError", error); } } ``` ================================================ FILE: obj/Debug/.NETFramework,Version=v4.7.1.AssemblyAttributes.cs ================================================ // using System; using System.Reflection; [assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] ================================================ FILE: strict-mode-sdk/strictMode.md ================================================ ## Strict Mode SDK * Use the [strict mode SDK](https://support.appsflyer.com/hc/en-us/articles/360001422989-User-opt-in-opt-out-in-the-AppsFlyer-SDK#strict-mode-sdk) to completely remove IDFA collection functionality and AdSupport framework dependencies (for example, when developing apps for kids). * [Strict mode](https://support.appsflyer.com/hc/en-us/articles/207032066-iOS-SDK-V6-X-integration-guide-for-developers#integration-strict-mode-sdk) SDK used pod `pod 'AppsFlyerFramework/Strict','6.1.3'` * In addition the following API are removed: `disableAdvertisingIdentifier` and `waitForATTUserAuthorizationWithTimeoutInterval` * To implement strict mode use the appsflyer-unity-plugin-\*.\*.\*-strict-mode.unitypackage