diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..96acc654 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,43 @@ +name: Plugin code analysis + +on: + push: + branches: + - "master" + paths-ignore: + - "**/README.md" + - "docs/**" + pull_request: + branches: + - "master" + - "dev" + +jobs: + check-lint-flutter: + name: Lint (flutter) + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup flutter + uses: subosito/flutter-action@v2 + with: + cache: true + - name: Lint + run: flutter format --output=none --set-exit-if-changed lib/ + check-code-analysis-flutter: + name: Code analysis (flutter) + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup flutter + uses: subosito/flutter-action@v2 + with: + cache: true + - name: Install dependencies + run: flutter pub get + - name: Analyze code + run: flutter analyze --no-fatal-infos \ No newline at end of file diff --git a/.github/workflows/deploy-apk.yaml b/.github/workflows/deploy-apk.yaml new file mode 100644 index 00000000..2a96c1df --- /dev/null +++ b/.github/workflows/deploy-apk.yaml @@ -0,0 +1,41 @@ +name: Create sample app APK + +on: +# pull_request: +# branches: +# - "master" +# paths-ignore: +# - "**/README.md" +# - "**/CHANGELOG.md" +# - "docs/**" + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + build: + name: Build APK + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - uses: actions/setup-java@v1 + with: + java-version: "12.x" + - uses: subosito/flutter-action@v1 + with: + channel: "stable" + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + cache: true + - name: Make executeable build-apk.sh + run: chmod +x tool/build-apk.sh + - name: Install dependencies & Build apk + run: tool/build-apk.sh + - uses: actions/upload-artifact@v1 + with: + name: release-apk + path: ./example/build/app/outputs/apk/release/app-release.apk \ No newline at end of file diff --git a/.github/workflows/deploy-web.yaml b/.github/workflows/deploy-web.yaml new file mode 100644 index 00000000..cdfab311 --- /dev/null +++ b/.github/workflows/deploy-web.yaml @@ -0,0 +1,47 @@ +name: Deploying the sample app to the web + +on: +# pull_request: +# branches: +# - "master" +# paths-ignore: +# - "**/README.md" +# - "**/CHANGELOG.md" +# - "docs/**" + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + build: + name: Build Web + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + cache: true + - name: Make executeable build-web.sh + run: chmod +x tool/build-web.sh + - name: Install dependencies & Build web + run: tool/build-web.sh + - name: Deploy to Netlify + if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} + uses: nwtgck/actions-netlify@v1.1 + with: + publish-dir: './example/build/web' + production-branch: master + production-deploy: true + github-token: ${{ secrets.GITHUB_TOKEN }} + deploy-message: "Deploy from GitHub Actions" + enable-pull-request-comment: false + enable-commit-comment: true + overwrites-pull-request-comment: true + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} + timeout-minutes: 1 \ No newline at end of file diff --git a/.github/workflows/pr-master-bugfix.yaml b/.github/workflows/pr-master-bugfix.yaml new file mode 100644 index 00000000..ca07a7d2 --- /dev/null +++ b/.github/workflows/pr-master-bugfix.yaml @@ -0,0 +1,24 @@ +name: PR Branch MASTER to BUGFIX + +on: + push: + branches: + - "master" + +jobs: + pull-request: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: pull-request-master-to-bugfix + if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} + uses: repo-sync/pull-request@v2.6.2 + with: + source_branch: "master" + destination_branch: "bugfix" + pr_title: "PR Branch MASTER into BUGFIX" + pr_body: "👑 *An automated PR*" + pr_label: "auto-pr" + pr_draft: false + pr_allow_empty: false + github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml new file mode 100644 index 00000000..1392f5fb --- /dev/null +++ b/.github/workflows/publish-plugin.yml @@ -0,0 +1,35 @@ +name: Publish plugin - pub.dev + +on: + push: +# branches: +# - "dev" + tags: + - 'v*' + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + cache: true + + - name: Publish flutter package + uses: k-paxian/dart-package-publisher@v1.4 +# uses: RodrigoSMarques/dart-package-publisher@master + with: + accessToken: ${{ secrets.PUBDEV_GOOGLE_ACCOUNT_ACCESS_TOKEN }} + refreshToken: ${{ secrets.PUBDEV_GOOGLE_ACCOUNT_REFRESH_TOKEN }} + flutter: true + format: true + skipTests: true + dryRunOnly: true +# docs: true + diff --git a/.github/workflows/send-dispatch.yaml b/.github/workflows/send-dispatch.yaml new file mode 100644 index 00000000..9d2e73a7 --- /dev/null +++ b/.github/workflows/send-dispatch.yaml @@ -0,0 +1,22 @@ +name: Send Dispatch to Branch fork + +on: + push: +# branches: +# - "master" +# - "dev" + tags: + - 'v*' + workflow_dispatch: + +jobs: + send-dispatch: + runs-on: ubuntu-latest + steps: + - name: Repository Dispatch + if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.TOKEN_BRANCH_UPSTREAM}} + repository: BranchMetrics/flutter_branch_sdk + event-type: new-release diff --git a/.github/workflows/sync-upstream.yaml b/.github/workflows/sync-upstream.yaml new file mode 100644 index 00000000..870c359d --- /dev/null +++ b/.github/workflows/sync-upstream.yaml @@ -0,0 +1,44 @@ +name: 'Upstream Sync' + +on: + repository_dispatch: + types: [new-release] + workflow_dispatch: # click the button on Github repo! + +jobs: + update_repository_branch: + runs-on: ubuntu-latest + steps: + - name: Repository master + uses: TobKed/github-forks-sync-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + upstream_repository: RodrigoSMarques/flutter_branch_sdk + #target_repository: BranchMetrics/flutter_branch_sdk + target_repository: r-s-marques/flutter_branch_sdk + upstream_branch: master + target_branch: master + force: true + tags: true + - name: Repository dev + uses: TobKed/github-forks-sync-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + upstream_repository: RodrigoSMarques/flutter_branch_sdk + #target_repository: BranchMetrics/flutter_branch_sdk + target_repository: r-s-marques/flutter_branch_sdk + upstream_branch: dev + target_branch: dev + force: true + tags: true + - name: Repository bugfix + uses: TobKed/github-forks-sync-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + upstream_repository: RodrigoSMarques/flutter_branch_sdk + #target_repository: BranchMetrics/flutter_branch_sdk + target_repository: r-s-marques/flutter_branch_sdk + upstream_branch: bugfix + target_branch: bugfix + force: true + tags: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c8969e..4f79e3ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## 6.0.0 +### BREAKING CHANGE +* Minimum required Dart SDK version to 2.17 (Flutter 3.0) +* Removed deprecated methods: + * `initWeb` + * `loadRewards` + * `redeemRewards` + * `getCreditHistory` + +### Enhancement +* New Methods: + - `getQRCodeAsData` + - `getQRCodeAsImage` + - `shareWithLPLinkMetadata` +* General improvements in code +* Fix analyzer code style warnings +* Updated Native `Android` and `iOS` SDKs: + * Android Native SDK Update 5.2.+ - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + * iOS Native SDK Update 1.43.+ - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) + + ## 5.1.1 * Updated Native `Android` SDK: * Android Native SDK Update 5.1.5 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) @@ -13,7 +34,7 @@ * iOS Native SDK Update 1.42.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) ## 5.0.0 -**BREAKING CHANGE**: +###BREAKING CHANGE: * `FlutterBranchSdk.initWeb` deprecated. * Branch for Flutter Web initialized in `index.html`, see `Web Integration` section @@ -87,7 +108,7 @@ * Added new method `getAdvertisingIdentifier`: Return Device Advertising Identifier ## 3.0.0 -* Initial support to Flutter Web . Thanks @mathatan +* Initial support to Flutter Web. Thanks @mathatan ## 2.0.0 * Stable null safety release. @@ -123,7 +144,8 @@ Updated Native ```Android``` and ```iOS``` SDKs * Android Native SDK Update 5.0.3 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) * iOS Native SDK Update 0.35.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) - __BREAKING CHANGES__ +###BREAKING CHANGES + Add KEY ```branch_check_apple_ads``` in INFO.PLIST to enable checking for Apple Search Ads before Branch initialization ## 1.0.0 diff --git a/LICENSE b/LICENSE index ab866628..928edc82 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ 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. +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 8f4b4445..3e7797b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# flutter_branch_sdk +# Branch SDK Plugin + +[![Branch](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/branch.png?raw=true)](https://branch.io) This is a Flutter plugin that implemented [Branch SDK](https://branch.io). @@ -6,8 +8,8 @@ Branch.io helps mobile apps grow with deep links that power referral systems, sh Supports Android, iOS and Web. -* Android - Branch SDK Version >= 5.1.5 [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) -* iOS - Branch SDK Version >= 1.42.0 [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) +* Android - Branch SDK Version >= 5.2.+ [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) +* iOS - Branch SDK Version >= 1.43.+ [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) Implemented functions in plugin: @@ -23,7 +25,9 @@ List BUO on Search / Remove BUO from Search| X | X | Not supported Register view| X | X | X Track User Actions and Events| X | X | X Init Branch Session and Deep Link| X | X | X -Referral rewards| X | X | X +Last Attributed Touch Data| X | X | X +QR codes| X | X | X +Share with LPLinkMetadata | | X | ## Getting Started ### Configure Branch Dashboard @@ -184,9 +188,7 @@ To listen to the clicks on the deep link and retrieve the data it is necessary t print('Custom string: ${data["custom_string"]}'); } }, onError: (error) { - PlatformException platformException = error as PlatformException; - print( - 'InitSession error: ${platformException.code} - ${platformException.message}'); + print('InitSesseion error: ${error.toString()}'); }); ``` @@ -198,13 +200,32 @@ If you ever want to access the original session params (the parameters passed in ```dart Map params = await FlutterBranchSdk.getFirstReferringParams(); ``` + ### Retrieve session (install or open) parameters These session parameters will be available at any point later on with this command. If no parameters are available then Branch will return an empty dictionary. This refreshes with every new session (app installs AND app opens). ```dart Map params = await FlutterBranchSdk.getLatestReferringParams(); ``` -### Create content reference + +### Retrieve Branch's Last Attributed Touch Data + +Allow retrieval of our last attributed touch data (LATD) from the client. This results in an asynchronous call being made to Branch’s servers with LATD data returned when possible. + +Last attributed touch data contains the information associated with that user's last viewed impression or clicked link. + + +```dart +BranchResponse response = + await FlutterBranchSdk.getLastAttributedTouchData(); + if (response.success) { + print(response.result.toString()); + } +``` + +More information [here](https://help.branch.io/developers-hub/docs/retrieving-branchs-last-attributed-touch-data). + +### Create content reference (Branch Universal Object) The Branch Universal Object encapsulates the thing you want to share. ```dart @@ -212,7 +233,7 @@ The Branch Universal Object encapsulates the thing you want to share. canonicalIdentifier: 'flutter/branch', //canonicalUrl: '', title: 'Flutter Branch Plugin', - imageUrl: 'https://flutter.dev/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg', + imageUrl: 'https://raw.githubusercontent.com/RodrigoSMarques/flutter_branch_sdk/master/assets/branch_logo_qrcode.jpeg', contentDescription: 'Flutter Branch Description', keywords: ['Plugin', 'Branch', 'Flutter'], publiclyIndex: true, @@ -229,9 +250,9 @@ The Branch Universal Object encapsulates the thing you want to share. > If your content lives both on the web and in the app, make sure you set its canonical URL (i.e. the URL of this piece of content on the web) when building any BUO. > By doing so, we’ll attribute clicks on the links that you generate back to their original web page, even if the user goes to the app instead of your website! This will help your SEO efforts. -More information about the parameters check [Android documentation](https://help.branch.io/developers-hub/docs/android-full-reference#parameters) and [iOS documentation](https://help.branch.io/developers-hub/docs/ios-full-reference#methods-and-properties) +More information about the parameters, verify [Android documentation](https://help.branch.io/developers-hub/docs/android-full-reference#parameters) and [iOS documentation](https://help.branch.io/developers-hub/docs/ios-full-reference#methods-and-properties) -### Create link reference +### Create link reference (BranchLinkProperties) * Generates the analytical properties for the deep link. * Used for Create deep link and Share deep link. @@ -252,9 +273,8 @@ More information about the parameters check [Android documentation](https://help > For example, instead of a random string of characters/integers, you can set the vanity alias as \*.app.link/devonaustin. > Aliases are enforced to be unique and immutable per domain, and per link - they cannot be reused unless deleted. -More information about the parameters check [Android documentation](https://help.branch.io/developers-hub/docs/android-full-reference#creating-a-deep-link) and [iOS documentation](https://help.branch.io/developers-hub/docs/ios-full-reference#link-properties-parameters) - - +More information about the parameters, verify [Android documentation](https://help.branch.io/developers-hub/docs/android-full-reference#creating-a-deep-link) and [iOS documentation](https://help.branch.io/developers-hub/docs/ios-full-reference#link-properties-parameters) + ### Create deep link Generates a deep link within your app. @@ -267,7 +287,7 @@ Generates a deep link within your app. print('Error : ${response.errorCode} - ${response.errorMessage}'); } ``` -### Show Share Sheet deep link +### Show Share Sheet with deep link Will generate a Branch deep link and tag it with the channel the user selects. > Note: _For Android additional customization is possible_ @@ -285,17 +305,95 @@ Will generate a Branch deep link and tag it with the channel the user selects. print('Error : ${response.errorCode} - ${response.errorMessage}'); } ``` + +### Show Share Sheet with LPLinkMetadata +> Note: _Requires iOS 13 or higher, else call showShareSheet `function`_ + +Will show Share Sheet with customization. + +#### Parameters +1. Content - verify section [Create content reference](#Create-content-reference) + +2. Link Reference - verify section [Create link reference](#Create-link-reference) + +3. Title (String) - Title for Share Sheet + +3. Icon (Uint8List) - Image for Share Sheet. Load image before from Web or assets. + + +```dart + FlutterBranchSdk.shareWithLPLinkMetadata( + buo: buo!, + linkProperties: lp, + title: "Share With LPLinkMetadata", + icon: iconData); +``` + +### Create a QR Code + +> **QR Code Access Required** +> +> Access to Branch's QR Code API and SDK requires premium product access. +> Please reach out to your account manager or [https://branch.io/pricing/](https://branch.io/pricing/) to activate. + + +Will generates a custom QR Code with a unique Branch link which you can deep link and track analytics with. + +#### Parameters +1. Content - verify section [Create content reference](#create-content-reference) + +2. Link Reference - verify section [Create link reference](#create-link-reference) + +3. BranchQrCode object (QR Code settings) + +Parameter | Type | Definition +--- | --- | --- +primaryColor | Color | Color name ou Hex color value +backgroundColor | Color | Color name ou Hex color value of the background of the QR code itself. +margin|Integer (Pixels)|The number of pixels you want for the margin. Min 1px. Max 20px. +width|Integer (Pixels)|Output size of QR Code image. Min 300px. Max 2000px. (Only applicable to JPEG/PNG) +imageFormat|BranchImageFormat|JPEG, PNG +centerLogoUrl|String (HTTP URL)|URL to the image you want as a center logo e.g. [https://raw.githubusercontent.com/RodrigoSMarques/flutter_branch_sdk/master/assets/branch_logo_qrcode.jpeg](https://raw.githubusercontent.com/RodrigoSMarques/flutter_branch_sdk/master/assets/branch_logo_qrcode.jpeg) + +```dart + BranchResponse responseQrCodeImage = + await FlutterBranchSdk.getQRCodeAsImage( + buo: buo!, + linkProperties: lp, + qrCode: BranchQrCode( + primaryColor: Colors.black, + //primaryColor: const Color(0xff443a49), //Hex colors + centerLogoUrl: imageURL, + backgroundColor: Colors.white, + imageFormat: BranchImageFormat.PNG)); + + if (response.success) { + print('QrCode Success'); + showQrCode(this.context, responseQrCodeImage.result); + /* + Image( + image: responseQrCodeImage.result, + height: 250, + width: 250, + ), + */ + } else { + print('Error : ${response.errorCode} - ${response.errorMessage}'); + +``` + +- Method `getQRCodeAsImage` returns the QR code as a Image. +- Method `getQRCodeAsData` returns the QR code as Uint8List. Can be stored in a file or converted to image. + ### List content on Search -* For Android list BUO links in Google Search with App Indexing +* For Android list BUO links in Google Search with Firebase App Indexing API and locally in Google In Apps search * For iOs list BUO links in Spotlight -Enable automatic sitemap generation on the Organic Search page of the [Branch Dashboard](https://dashboard.branch.io/search). -Check the Automatic sitemap generation checkbox. - ```dart bool success = await FlutterBranchSdk.listOnSearch(buo: buo); print(success); ``` + ### Remove content from Search Privately indexed Branch Universal Object can be removed. @@ -405,23 +503,6 @@ By default, Branch limits calls to SKAdNetwork to within 72 hours after first in FlutterBranchSdk.setIOSSKAdNetworkMaxTime(24); ``` -### Retrieve Branch's Last Attributed Touch Data - -Allow retrieval of our last attributed touch data (LATD) from the client. This results in an asynchronous call being made to Branch’s servers with LATD data returned when possible. - -Last attributed touch data contains the information associated with that user's last viewed impression or clicked link. - - -```dart -BranchResponse response = - await FlutterBranchSdk.getLastAttributedTouchData(); - if (response.success) { - print(response.result.toString()); - } -``` - -More information [here](https://help.branch.io/developers-hub/docs/retrieving-branchs-last-attributed-touch-data). - ### Apple Search Ads Branch can help track your Apple Search Ad campaigns by fetching the search ad attribution from Apple at app install. @@ -456,7 +537,7 @@ print(status); ``` > Note: After the user's response, call the `handleATTAuthorizationStatus` Branch SDK method to monitor the performance of the ATT prompt. -![App tracking dialog](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/dev/assets/app_tracking_dialog.png) +![App tracking dialog](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/app_tracking_dialog.png) #### Get tracking authorization status @@ -537,7 +618,7 @@ To enable: android:value="true" /> ``` -### iOS - Enabled Clipboard Deferred Deep Linking +### Enabled Clipboard Deferred Deep Linking in iOS Use iOS pasteboard to enable deferred deep linking. @@ -590,7 +671,7 @@ Follow the instructions to install Facebook Android / iOS SDK: # Getting Started See the `example` directory for a complete sample app using Branch SDK. -![Example app](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/dev/assets/example.png) +![Example app](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/example.png) See example in Flutter Web: [https://flutter-branch-sdk.netlify.app/](https://flutter-branch-sdk.netlify.app/#/) diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..8eed5e63 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,7 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options +linter: + rules: + constant_identifier_names: false \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore index c6cbe562..a2b1dea3 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,8 +1,9 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/android/build.gradle b/android/build.gradle index 07fc2ddf..63a0c1b8 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,42 +1,43 @@ -group 'br.com.rsmarques.flutter_branch_sdk' -version '1.0' - -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' - -android { - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 21 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - } -} - -dependencies { - implementation 'io.branch.sdk.android:library:5.1.5' - implementation 'com.google.firebase:firebase-appindexing:19.0.0' - implementation 'com.google.android.gms:play-services-ads-identifier:17.1.0+' - implementation 'androidx.browser:browser:1.4.0' - implementation 'androidx.lifecycle:lifecycle-runtime:2.4.0' +group 'br.com.rsmarques.flutter_branch_sdk' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 21 + } +} + +dependencies { + implementation 'io.branch.sdk.android:library:5.2.+' + implementation 'com.google.firebase:firebase-appindexing:19.0.0' + implementation 'com.google.android.gms:play-services-ads-identifier:17.1.0+' + implementation 'androidx.browser:browser:1.4.0' + implementation 'androidx.lifecycle:lifecycle-runtime:2.4.1' } \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 38c8d454..00000000 --- a/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9fe8d05d..00000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 8b8035fd..989ef1a9 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'flutter_branch_sdk' +rootProject.name = 'flutter_branch_sdk' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 014c920c..e0c7b550 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - - - + + + diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/ApplicationInfoHelper.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/ApplicationInfoHelper.java index 82ed09b2..532cb9f8 100644 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/ApplicationInfoHelper.java +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/ApplicationInfoHelper.java @@ -1,50 +1,50 @@ -package br.com.rsmarques.flutter_branch_sdk; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; - -import io.flutter.BuildConfig; - -public class ApplicationInfoHelper { - private static Context context; - - ApplicationInfoHelper(Context context) { - this.context = context; - } - - public static boolean getEnableLog() { - try { - final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - if (ai.metaData != null) { - if (BuildConfig.DEBUG) { - return ai.metaData.getBoolean("branch_enable_log", - true); - } else { - return ai.metaData.getBoolean("branch_enable_log", - false); - } - } else { - return BuildConfig.DEBUG; - } - } catch (Exception e) { - LogUtils.debug("FlutterBranchSDK", "ApplicationInfoHelper error: " + e.getLocalizedMessage()); - } - return false; - } - - public static boolean getEnableFacebookAds() { - try { - final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); - if (ai.metaData != null) { - return ai.metaData.getBoolean("branch_enable_facebook_ads", - false); - } else { - return false; - } - } catch (Exception e) { - LogUtils.debug("FlutterBranchSDK", "ApplicationInfoHelper error: " + e.getLocalizedMessage()); - } - return false; - } -} +package br.com.rsmarques.flutter_branch_sdk; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; + +import io.flutter.BuildConfig; + +public class ApplicationInfoHelper { + private static Context context; + + ApplicationInfoHelper(Context context) { + this.context = context; + } + + public static boolean getEnableLog() { + try { + final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); + if (ai.metaData != null) { + if (BuildConfig.DEBUG) { + return ai.metaData.getBoolean("branch_enable_log", + true); + } else { + return ai.metaData.getBoolean("branch_enable_log", + false); + } + } else { + return BuildConfig.DEBUG; + } + } catch (Exception e) { + LogUtils.debug("FlutterBranchSDK", "ApplicationInfoHelper error: " + e.getLocalizedMessage()); + } + return false; + } + + public static boolean getEnableFacebookAds() { + try { + final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); + if (ai.metaData != null) { + return ai.metaData.getBoolean("branch_enable_facebook_ads", + false); + } else { + return false; + } + } catch (Exception e) { + LogUtils.debug("FlutterBranchSDK", "ApplicationInfoHelper error: " + e.getLocalizedMessage()); + } + return false; + } +} diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkHelper.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkHelper.java index 08657d23..f5be7455 100644 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkHelper.java +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkHelper.java @@ -1,253 +1,282 @@ -package br.com.rsmarques.flutter_branch_sdk; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import io.branch.indexing.BranchUniversalObject; -import io.branch.referral.util.AdType; -import io.branch.referral.util.BRANCH_STANDARD_EVENT; -import io.branch.referral.util.BranchContentSchema; -import io.branch.referral.util.BranchEvent; -import io.branch.referral.util.ContentMetadata; -import io.branch.referral.util.CurrencyType; -import io.branch.referral.util.LinkProperties; -import io.branch.referral.util.ProductCategory; - -public class FlutterBranchSdkHelper { - /**--------------------------------------------------------------------------------------------- - Object Conversion Functions - --------------------------------------------------------------------------------------------**/ - BranchUniversalObject convertToBUO(HashMap argsMap) { - - BranchUniversalObject buo = new BranchUniversalObject(); - String canonicalIdentifier = (String) argsMap.get("canonicalIdentifier"); - buo.setCanonicalIdentifier(canonicalIdentifier); - - if (argsMap.containsKey("canonicalUrl")) - buo.setCanonicalUrl((String) argsMap.get("canonicalUrl")); - if (argsMap.containsKey("title")) - buo.setTitle((String) argsMap.get("title")); - if (argsMap.containsKey("contentDescription")) - buo.setContentDescription((String) argsMap.get("contentDescription")); - if (argsMap.containsKey("imageUrl")) - buo.setContentImageUrl((String) argsMap.get("imageUrl")); - if (argsMap.containsKey("keywords")) - buo.addKeyWords((ArrayList) argsMap.get("keywords")); - if (argsMap.containsKey("expirationDate")) - buo.setContentExpiration(new Date((long) argsMap.get("expirationDate"))); - - if (argsMap.containsKey("locallyIndex")) { - boolean value = (boolean) argsMap.get("locallyIndex"); - if (value) { - buo.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC); - } else - buo.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE); - } - if (argsMap.containsKey("publiclyIndex")) { - boolean value = (boolean) argsMap.get("publiclyIndex"); - if (value) { - buo.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC); - } else - buo.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE); - } - if (argsMap.containsKey("contentMetadata")) { - HashMap contentMap = (HashMap) argsMap.get("contentMetadata"); - ContentMetadata contentMetadata = new ContentMetadata(); - if (contentMap.containsKey("quantity")) - contentMetadata.setQuantity((double) contentMap.get("quantity")); - if (contentMap.containsKey("price") && contentMap.containsKey("currency")) { - contentMetadata.setPrice((double) contentMap.get("price"), CurrencyType.getValue((String) contentMap.get("currency"))); - } - if (contentMap.containsKey("rating_average") || contentMap.containsKey("rating_count") || - contentMap.containsKey("rating_max") || contentMap.containsKey("rating")) { - Double rating = null; - if (contentMap.containsKey("rating")) { - rating = (double) contentMap.get("rating"); - } - Double rating_average = null; - if (contentMap.containsKey("rating_average")) { - rating_average = (double) contentMap.get("rating_average"); - } - Integer rating_count = null; - if (contentMap.containsKey("rating_count")) { - rating_count = (Integer) contentMap.get("rating_count"); - } - Double rating_max = null; - if (contentMap.containsKey("rating_max")) { - rating_max = (double) contentMap.get("rating_max"); - } - contentMetadata.setRating(rating, rating_average, rating_max, rating_count); - } - if (contentMap.containsKey("latitude") && contentMap.containsKey("longitude")) { - contentMetadata.setLocation((double) contentMap.get("latitude"), (double) contentMap.get("longitude")); - } - if (contentMap.containsKey("address_street") || contentMap.containsKey("address_city") || - contentMap.containsKey("address_region") || contentMap.containsKey("address_country") || contentMap.containsKey("address_postal_code")) { - String street = (String) contentMap.get("address_street"); - String city = (String) contentMap.get("address_city"); - String region = (String) contentMap.get("address_region"); - String country = (String) contentMap.get("address_country"); - String postal_code = (String) contentMap.get("address_postal_code"); - contentMetadata.setAddress(street, city, region, country, postal_code); - } - if (contentMap.containsKey("content_schema")) { - contentMetadata.setContentSchema(BranchContentSchema.getValue((String) contentMap.get("content_schema"))); - } - if (contentMap.containsKey("sku")) { - contentMetadata.setSku((String) contentMap.get("sku")); - } - if (contentMap.containsKey("product_name")) { - contentMetadata.setProductName((String) contentMap.get("product_name")); - } - if (contentMap.containsKey("product_brand")) { - contentMetadata.setProductBrand((String) contentMap.get("product_brand")); - } - if (contentMap.containsKey("product_category")) { - contentMetadata.setProductCategory(ProductCategory.getValue((String) contentMap.get("product_category"))); - } - if (contentMap.containsKey("product_variant")) { - contentMetadata.setProductVariant((String) contentMap.get("product_variant")); - } - if (contentMap.containsKey("condition")) { - contentMetadata.setProductCondition(ContentMetadata.CONDITION.getValue((String) contentMap.get("product_category"))); - } - if (contentMap.containsKey("image_captions")) { - ArrayList _imageCaptions = (ArrayList) contentMap.get("image_captions"); - for (int i = 0; i < _imageCaptions.size(); i++) { - contentMetadata.addImageCaptions(_imageCaptions.get(i)); - } - } - if (contentMap.containsKey("customMetadata")) { - for (Map.Entry customMetaData : ((HashMap) contentMap.get("customMetadata")).entrySet()) { - contentMetadata.addCustomMetadata(customMetaData.getKey(), customMetaData.getValue().toString()); - } - } - buo.setContentMetadata(contentMetadata); - } - return buo; - } - - LinkProperties convertToLinkProperties(HashMap argsMap) { - - LinkProperties linkProperties = new LinkProperties(); - - if (argsMap.containsKey("channel")) - linkProperties.setChannel((String) argsMap.get("channel")); - if (argsMap.containsKey("feature")) - linkProperties.setFeature((String) argsMap.get("feature")); - if (argsMap.containsKey("campaign")) - linkProperties.setCampaign((String) argsMap.get("campaign")); - if (argsMap.containsKey("stage")) - linkProperties.setStage((String) argsMap.get("stage")); - if (argsMap.containsKey("alias")) - linkProperties.setAlias((String) argsMap.get("alias")); - if (argsMap.containsKey("matchDuration")) - linkProperties.setDuration((int) argsMap.get("matchDuration")); - if (argsMap.containsKey("tags")) { - ArrayList _tags = (ArrayList) argsMap.get("tags"); - for (int i = 0; i < _tags.size(); i++) { - linkProperties.addTag(_tags.get(i)); - } - } - if (argsMap.containsKey("controlParams")) { - for (Map.Entry content : ((HashMap) argsMap.get("controlParams")).entrySet()) { - linkProperties.addControlParameter(content.getKey(), content.getValue()); - } - } - return linkProperties; - } - - BranchEvent convertToEvent(HashMap eventMap) { - BranchEvent event; - - if ((boolean) eventMap.get("isStandardEvent")) { - event = new BranchEvent(BRANCH_STANDARD_EVENT.valueOf((String) eventMap.get("eventName"))); - } else { - event = new BranchEvent((String) eventMap.get("eventName")); - } - - if (eventMap.containsKey("transactionID")) - event.setTransactionID((String) eventMap.get("transactionID")); - if (eventMap.containsKey("currency")) - event.setCurrency(CurrencyType.getValue((String) eventMap.get("currency"))); - if (eventMap.containsKey("revenue")) - event.setRevenue((Double) eventMap.get("revenue")); - if (eventMap.containsKey("shipping")) - event.setShipping((Double) eventMap.get("shipping")); - if (eventMap.containsKey("tax")) - event.setTax((Double) eventMap.get("tax")); - if (eventMap.containsKey("coupon")) - event.setCoupon((String) eventMap.get("coupon")); - if (eventMap.containsKey("affiliation")) - event.setAffiliation((String) eventMap.get("affiliation")); - if (eventMap.containsKey("eventDescription")) - event.setDescription((String) eventMap.get("eventDescription")); - if (eventMap.containsKey("searchQuery")) - event.setSearchQuery((String) eventMap.get("searchQuery")); - if (eventMap.containsKey("adType")) - event.setAdType(convertToAdType((String) eventMap.get("adType"))); - if (eventMap.containsKey("customData")) { - for (Map.Entry customData : ((HashMap) eventMap.get("customData")).entrySet()) { - event.addCustomDataProperty(customData.getKey(), customData.getValue()); - } - } - return event; - } - - AdType convertToAdType(String adType) { - switch (adType) { - case "BANNER": - return AdType.BANNER; - case "INTERSTITIAL": - return AdType.INTERSTITIAL; - case "REWARDED_VIDEO": - return AdType.REWARDED_VIDEO; - case "NATIVE": - return AdType.NATIVE; - default: - throw new IllegalStateException("Unexpected value: " + adType); - } - } - - //---------------------------------------------------------------------------------------------- - - Map paramsToMap(JSONObject jsonObject) throws JSONException { - Map map = new HashMap<>(); - Iterator keys = jsonObject.keys(); - while (keys.hasNext()) { - String key = keys.next(); - Object value = jsonObject.get(key); - if (value instanceof JSONArray) { - value = jsonArrayToList((JSONArray) value); - } else if (value instanceof JSONObject) { - value = paramsToMap((JSONObject) value); - } - map.put(key, value); - } - return map; - } - - List jsonArrayToList(JSONArray array) throws JSONException { - List list = new ArrayList<>(); - for (int i = 0; i < array.length(); i++) { - Object value = array.get(i); - if (value instanceof JSONArray) { - value = jsonArrayToList((JSONArray) value); - } else if (value instanceof JSONObject) { - value = paramsToMap((JSONObject) value); - } - list.add(value); - } - return list; - } -} +package br.com.rsmarques.flutter_branch_sdk; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import io.branch.indexing.BranchUniversalObject; +import io.branch.referral.QRCode.BranchQRCode; +import io.branch.referral.util.AdType; +import io.branch.referral.util.BRANCH_STANDARD_EVENT; +import io.branch.referral.util.BranchContentSchema; +import io.branch.referral.util.BranchEvent; +import io.branch.referral.util.ContentMetadata; +import io.branch.referral.util.CurrencyType; +import io.branch.referral.util.LinkProperties; +import io.branch.referral.util.ProductCategory; + +public class FlutterBranchSdkHelper { + /**--------------------------------------------------------------------------------------------- + Object Conversion Functions + --------------------------------------------------------------------------------------------**/ + BranchUniversalObject convertToBUO(HashMap argsMap) { + + BranchUniversalObject buo = new BranchUniversalObject(); + String canonicalIdentifier = (String) argsMap.get("canonicalIdentifier"); + buo.setCanonicalIdentifier(canonicalIdentifier); + + if (argsMap.containsKey("canonicalUrl")) + buo.setCanonicalUrl((String) argsMap.get("canonicalUrl")); + if (argsMap.containsKey("title")) + buo.setTitle((String) argsMap.get("title")); + if (argsMap.containsKey("contentDescription")) + buo.setContentDescription((String) argsMap.get("contentDescription")); + if (argsMap.containsKey("imageUrl")) + buo.setContentImageUrl((String) argsMap.get("imageUrl")); + if (argsMap.containsKey("keywords")) + buo.addKeyWords((ArrayList) argsMap.get("keywords")); + if (argsMap.containsKey("expirationDate")) + buo.setContentExpiration(new Date((long) argsMap.get("expirationDate"))); + + if (argsMap.containsKey("locallyIndex")) { + boolean value = (boolean) argsMap.get("locallyIndex"); + if (value) { + buo.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC); + } else + buo.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE); + } + if (argsMap.containsKey("publiclyIndex")) { + boolean value = (boolean) argsMap.get("publiclyIndex"); + if (value) { + buo.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC); + } else + buo.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE); + } + if (argsMap.containsKey("contentMetadata")) { + HashMap contentMap = (HashMap) argsMap.get("contentMetadata"); + ContentMetadata contentMetadata = new ContentMetadata(); + if (contentMap.containsKey("quantity")) + contentMetadata.setQuantity((double) contentMap.get("quantity")); + if (contentMap.containsKey("price") && contentMap.containsKey("currency")) { + contentMetadata.setPrice((double) contentMap.get("price"), CurrencyType.getValue((String) contentMap.get("currency"))); + } + if (contentMap.containsKey("rating_average") || contentMap.containsKey("rating_count") || + contentMap.containsKey("rating_max") || contentMap.containsKey("rating")) { + Double rating = null; + if (contentMap.containsKey("rating")) { + rating = (double) contentMap.get("rating"); + } + Double rating_average = null; + if (contentMap.containsKey("rating_average")) { + rating_average = (double) contentMap.get("rating_average"); + } + Integer rating_count = null; + if (contentMap.containsKey("rating_count")) { + rating_count = (Integer) contentMap.get("rating_count"); + } + Double rating_max = null; + if (contentMap.containsKey("rating_max")) { + rating_max = (double) contentMap.get("rating_max"); + } + contentMetadata.setRating(rating, rating_average, rating_max, rating_count); + } + if (contentMap.containsKey("latitude") && contentMap.containsKey("longitude")) { + contentMetadata.setLocation((double) contentMap.get("latitude"), (double) contentMap.get("longitude")); + } + if (contentMap.containsKey("address_street") || contentMap.containsKey("address_city") || + contentMap.containsKey("address_region") || contentMap.containsKey("address_country") || contentMap.containsKey("address_postal_code")) { + String street = (String) contentMap.get("address_street"); + String city = (String) contentMap.get("address_city"); + String region = (String) contentMap.get("address_region"); + String country = (String) contentMap.get("address_country"); + String postal_code = (String) contentMap.get("address_postal_code"); + contentMetadata.setAddress(street, city, region, country, postal_code); + } + if (contentMap.containsKey("content_schema")) { + contentMetadata.setContentSchema(BranchContentSchema.getValue((String) contentMap.get("content_schema"))); + } + if (contentMap.containsKey("sku")) { + contentMetadata.setSku((String) contentMap.get("sku")); + } + if (contentMap.containsKey("product_name")) { + contentMetadata.setProductName((String) contentMap.get("product_name")); + } + if (contentMap.containsKey("product_brand")) { + contentMetadata.setProductBrand((String) contentMap.get("product_brand")); + } + if (contentMap.containsKey("product_category")) { + contentMetadata.setProductCategory(ProductCategory.getValue((String) contentMap.get("product_category"))); + } + if (contentMap.containsKey("product_variant")) { + contentMetadata.setProductVariant((String) contentMap.get("product_variant")); + } + if (contentMap.containsKey("condition")) { + contentMetadata.setProductCondition(ContentMetadata.CONDITION.getValue((String) contentMap.get("product_category"))); + } + if (contentMap.containsKey("image_captions")) { + ArrayList _imageCaptions = (ArrayList) contentMap.get("image_captions"); + for (int i = 0; i < _imageCaptions.size(); i++) { + contentMetadata.addImageCaptions(_imageCaptions.get(i)); + } + } + if (contentMap.containsKey("customMetadata")) { + for (Map.Entry customMetaData : ((HashMap) contentMap.get("customMetadata")).entrySet()) { + contentMetadata.addCustomMetadata(customMetaData.getKey(), customMetaData.getValue().toString()); + } + } + buo.setContentMetadata(contentMetadata); + } + return buo; + } + + LinkProperties convertToLinkProperties(HashMap argsMap) { + + LinkProperties linkProperties = new LinkProperties(); + + if (argsMap.containsKey("channel")) + linkProperties.setChannel((String) argsMap.get("channel")); + if (argsMap.containsKey("feature")) + linkProperties.setFeature((String) argsMap.get("feature")); + if (argsMap.containsKey("campaign")) + linkProperties.setCampaign((String) argsMap.get("campaign")); + if (argsMap.containsKey("stage")) + linkProperties.setStage((String) argsMap.get("stage")); + if (argsMap.containsKey("alias")) + linkProperties.setAlias((String) argsMap.get("alias")); + if (argsMap.containsKey("matchDuration")) + linkProperties.setDuration((int) argsMap.get("matchDuration")); + if (argsMap.containsKey("tags")) { + ArrayList _tags = (ArrayList) argsMap.get("tags"); + for (int i = 0; i < _tags.size(); i++) { + linkProperties.addTag(_tags.get(i)); + } + } + if (argsMap.containsKey("controlParams")) { + for (Map.Entry content : ((HashMap) argsMap.get("controlParams")).entrySet()) { + linkProperties.addControlParameter(content.getKey(), content.getValue()); + } + } + return linkProperties; + } + + BranchEvent convertToEvent(HashMap eventMap) { + BranchEvent event; + + if ((boolean) eventMap.get("isStandardEvent")) { + event = new BranchEvent(BRANCH_STANDARD_EVENT.valueOf((String) eventMap.get("eventName"))); + } else { + event = new BranchEvent((String) eventMap.get("eventName")); + } + + if (eventMap.containsKey("transactionID")) + event.setTransactionID((String) eventMap.get("transactionID")); + if (eventMap.containsKey("currency")) + event.setCurrency(CurrencyType.getValue((String) eventMap.get("currency"))); + if (eventMap.containsKey("revenue")) + event.setRevenue((Double) eventMap.get("revenue")); + if (eventMap.containsKey("shipping")) + event.setShipping((Double) eventMap.get("shipping")); + if (eventMap.containsKey("tax")) + event.setTax((Double) eventMap.get("tax")); + if (eventMap.containsKey("coupon")) + event.setCoupon((String) eventMap.get("coupon")); + if (eventMap.containsKey("affiliation")) + event.setAffiliation((String) eventMap.get("affiliation")); + if (eventMap.containsKey("eventDescription")) + event.setDescription((String) eventMap.get("eventDescription")); + if (eventMap.containsKey("searchQuery")) + event.setSearchQuery((String) eventMap.get("searchQuery")); + if (eventMap.containsKey("adType")) + event.setAdType(convertToAdType((String) eventMap.get("adType"))); + if (eventMap.containsKey("customData")) { + for (Map.Entry customData : ((HashMap) eventMap.get("customData")).entrySet()) { + event.addCustomDataProperty(customData.getKey(), customData.getValue()); + } + } + return event; + } + + BranchQRCode convertToQRCode(HashMap qrCodeMap) { + BranchQRCode branchQRCode = new BranchQRCode(); + if (qrCodeMap.containsKey("width")) { + branchQRCode.setWidth((int) qrCodeMap.get("width")); + } + if (qrCodeMap.containsKey("margin")) { + branchQRCode.setMargin((int) qrCodeMap.get("margin")); + } + if (qrCodeMap.containsKey("codeColor")) { + branchQRCode.setCodeColor((String) qrCodeMap.get("codeColor")); + } + if (qrCodeMap.containsKey("backgroundColor")) { + branchQRCode.setBackgroundColor((String) qrCodeMap.get("backgroundColor")); + } + if (qrCodeMap.containsKey("imageFormat")) { + final String imageFormat = (String) qrCodeMap.get("imageFormat"); + if (imageFormat.equals("JPEG")) { + branchQRCode.setImageFormat(BranchQRCode.BranchImageFormat.JPEG); + } else { + branchQRCode.setImageFormat(BranchQRCode.BranchImageFormat.PNG); + } + } + if (qrCodeMap.containsKey("centerLogoUrl")) { + branchQRCode.setCenterLogo((String) qrCodeMap.get("centerLogoUrl")); + } + return branchQRCode; + } + + AdType convertToAdType(String adType) { + switch (adType) { + case "BANNER": + return AdType.BANNER; + case "INTERSTITIAL": + return AdType.INTERSTITIAL; + case "REWARDED_VIDEO": + return AdType.REWARDED_VIDEO; + case "NATIVE": + return AdType.NATIVE; + default: + throw new IllegalStateException("Unexpected value: " + adType); + } + } + + //---------------------------------------------------------------------------------------------- + + Map paramsToMap(JSONObject jsonObject) throws JSONException { + Map map = new HashMap<>(); + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jsonObject.get(key); + if (value instanceof JSONArray) { + value = jsonArrayToList((JSONArray) value); + } else if (value instanceof JSONObject) { + value = paramsToMap((JSONObject) value); + } + map.put(key, value); + } + return map; + } + + List jsonArrayToList(JSONArray array) throws JSONException { + List list = new ArrayList<>(); + for (int i = 0; i < array.length(); i++) { + Object value = array.get(i); + if (value instanceof JSONArray) { + value = jsonArrayToList((JSONArray) value); + } else if (value instanceof JSONObject) { + value = paramsToMap((JSONObject) value); + } + list.add(value); + } + return list; + } +} diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkInit.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkInit.java index 1160501e..3d3d1e59 100644 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkInit.java +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkInit.java @@ -1,31 +1,31 @@ -package br.com.rsmarques.flutter_branch_sdk; - -import android.content.Context; -import android.util.Log; - -import io.branch.referral.Branch; - -public class FlutterBranchSdkInit { - private static final String DEBUG_NAME = "FlutterBranchSDK"; - private static final String PLUGIN_NAME = "Flutter"; - private static final String PLUGIN_VERSION = "5.1.1"; - - public static void init(Context context) { - ApplicationInfoHelper applicationInfoHelper = new ApplicationInfoHelper(context); - - if (applicationInfoHelper.getEnableLog()) { - LogUtils.debug(DEBUG_NAME, "Branch SDK with log enable"); - Branch.enableLogging(); - } else { - Log.i(DEBUG_NAME, "Branch SDK with out log"); - } - - if (applicationInfoHelper.getEnableFacebookAds()) { - Branch.getAutoInstance(context).enableFacebookAppLinkCheck(); - } - - // Branch object initialization - Branch.registerPlugin(PLUGIN_NAME, PLUGIN_VERSION); - Branch.getAutoInstance(context); - } -} +package br.com.rsmarques.flutter_branch_sdk; + +import android.content.Context; +import android.util.Log; + +import io.branch.referral.Branch; + +public class FlutterBranchSdkInit { + private static final String DEBUG_NAME = "FlutterBranchSDK"; + private static final String PLUGIN_NAME = "Flutter"; + private static final String PLUGIN_VERSION = "6.0.0"; + + public static void init(Context context) { + ApplicationInfoHelper applicationInfoHelper = new ApplicationInfoHelper(context); + + if (applicationInfoHelper.getEnableLog()) { + LogUtils.debug(DEBUG_NAME, "Branch SDK with log enable"); + Branch.enableLogging(); + } else { + Log.i(DEBUG_NAME, "Branch SDK with out log"); + } + + if (applicationInfoHelper.getEnableFacebookAds()) { + Branch.getAutoInstance(context).enableFacebookAppLinkCheck(); + } + + // Branch object initialization + Branch.registerPlugin(PLUGIN_NAME, PLUGIN_VERSION); + Branch.getAutoInstance(context); + } +} diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkPlugin.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkPlugin.java index 1165d1f5..569c924c 100644 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkPlugin.java +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkPlugin.java @@ -1,956 +1,755 @@ -package br.com.rsmarques.flutter_branch_sdk; - -import android.app.Activity; -import android.app.Application; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import io.branch.indexing.BranchUniversalObject; -import io.branch.referral.Branch; -import io.branch.referral.BranchError; -import io.branch.referral.ServerRequestGetLATD; -import io.branch.referral.util.BranchEvent; -import io.branch.referral.util.LinkProperties; -import io.branch.referral.util.ShareSheetStyle; -import io.branch.referral.validators.IntegrationValidator; -import io.flutter.embedding.android.FlutterFragmentActivity; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.EventChannel; -import io.flutter.plugin.common.EventChannel.StreamHandler; -import io.flutter.plugin.common.EventChannel.EventSink; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import io.flutter.plugin.common.MethodChannel.MethodCallHandler; -import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry.NewIntentListener; - -public class FlutterBranchSdkPlugin implements FlutterPlugin, MethodCallHandler, StreamHandler, NewIntentListener, ActivityAware, - Application.ActivityLifecycleCallbacks { - private static final String DEBUG_NAME = "FlutterBranchSDK"; - private Activity activity; - private Context context; - private ActivityPluginBinding activityPluginBinding; - - private static final String MESSAGE_CHANNEL = "flutter_branch_sdk/message"; - private static final String EVENT_CHANNEL = "flutter_branch_sdk/event"; - private EventSink eventSink = null; - private Map initialParams = null; - private BranchError initialError = null; - - private final FlutterBranchSdkHelper branchSdkHelper = new FlutterBranchSdkHelper(); - - /** - * --------------------------------------------------------------------------------------------- - * Plugin registry - * -------------------------------------------------------------------------------------------- - **/ - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { - LogUtils.debug(DEBUG_NAME, "onAttachedToEngine call"); - setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext()); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - LogUtils.debug(DEBUG_NAME, "onDetachedFromEngine call"); - teardownChannels(); - } - - private void setupChannels(BinaryMessenger messenger, Context context) { - LogUtils.debug(DEBUG_NAME, "setupChannels call"); - this.context = context; - - MethodChannel methodChannel = new MethodChannel(messenger, MESSAGE_CHANNEL); - EventChannel eventChannel = new EventChannel(messenger, EVENT_CHANNEL); - - methodChannel.setMethodCallHandler(this); - eventChannel.setStreamHandler(this); - - FlutterBranchSdkInit.init(context); - } - - private void setActivity(Activity activity) { - LogUtils.debug(DEBUG_NAME, "setActivity call"); - this.activity = activity; - activity.getApplication().registerActivityLifecycleCallbacks(this); - - if (this.activity != null && FlutterFragmentActivity.class.isAssignableFrom(activity.getClass())) { - Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent() != null ? activity.getIntent().getData() : null).init(); - } - } - - private void teardownChannels() { - LogUtils.debug(DEBUG_NAME, "teardownChannels call"); - this.activityPluginBinding = null; - this.activity = null; - this.context = null; - } - - /** - * --------------------------------------------------------------------------------------------- - * ActivityAware Interface Methods - * -------------------------------------------------------------------------------------------- - **/ - @Override - public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) { - LogUtils.debug(DEBUG_NAME, "onAttachedToActivity call"); - this.activityPluginBinding = activityPluginBinding; - setActivity(activityPluginBinding.getActivity()); - activityPluginBinding.addOnNewIntentListener(this); - } - - @Override - public void onDetachedFromActivity() { - LogUtils.debug(DEBUG_NAME, "onDetachedFromActivity call"); - activityPluginBinding.removeOnNewIntentListener(this); - this.activity = null; - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - LogUtils.debug(DEBUG_NAME, "onDetachedFromActivityForConfigChanges call"); - onDetachedFromActivity(); - } - - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) { - LogUtils.debug(DEBUG_NAME, "onReattachedToActivityForConfigChanges call"); - onAttachedToActivity(activityPluginBinding); - } - - /** - * --------------------------------------------------------------------------------------------- - * StreamHandler Interface Methods - * -------------------------------------------------------------------------------------------- - **/ - @Override - public void onListen(Object o, EventChannel.EventSink eventSink) { - LogUtils.debug(DEBUG_NAME, "onListen call"); - this.eventSink = new MainThreadEventSink(eventSink); - if (initialParams != null) { - eventSink.success(initialParams); - initialParams = null; - initialError = null; - } else if (initialError != null) { - eventSink.error(String.valueOf(initialError.getErrorCode()), initialError.getMessage(), null); - initialParams = null; - initialError = null; - } - } - - @Override - public void onCancel(Object o) { - LogUtils.debug(DEBUG_NAME, "onCancel call"); - this.eventSink = new MainThreadEventSink(null); - initialError = null; - initialParams = null; - } - - /** - * --------------------------------------------------------------------------------------------- - * ActivityLifecycleCallbacks Interface Methods - * -------------------------------------------------------------------------------------------- - **/ - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - } - - @Override - public void onActivityStarted(Activity activity) { - LogUtils.debug(DEBUG_NAME, "onActivityStarted call"); - Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent() != null ? activity.getIntent().getData() : null).init(); - } - - @Override - public void onActivityResumed(Activity activity) { - } - - @Override - public void onActivityPaused(Activity activity) { - } - - @Override - public void onActivityStopped(Activity activity) { - LogUtils.debug(DEBUG_NAME, "onActivityStopped call"); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - LogUtils.debug(DEBUG_NAME, "onActivityDestroyed call"); - if (this.activity == activity) { - activity.getApplication().unregisterActivityLifecycleCallbacks(this); - } - } - - /** - * --------------------------------------------------------------------------------------------- - * NewIntentListener Interface Methods - * -------------------------------------------------------------------------------------------- - **/ - @Override - public boolean onNewIntent(Intent intent) { - LogUtils.debug(DEBUG_NAME, "onNewIntent call"); - if (this.activity != null) { - this.activity.setIntent(intent); - - if (intent != null && - intent.hasExtra("branch_force_new_session") && - intent.getBooleanExtra("branch_force_new_session", false)) { - Branch.sessionBuilder(this.activity).withCallback(branchReferralInitListener).reInit(); - } - return true; - } - return false; - } - - /** - * --------------------------------------------------------------------------------------------- - * MethodCallHandler Interface Methods - * -------------------------------------------------------------------------------------------- - **/ - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result rawResult) { - Result result = new MethodResultWrapper(rawResult); - switch (call.method) { - case "getShortUrl": - getShortUrl(call, result); - break; - case "showShareSheet": - showShareSheet(call, result); - break; - case "registerView": - registerView(call); - break; - case "listOnSearch": - listOnSearch(call, result); - break; - case "removeFromSearch": - removeFromSearch(call, result); - break; - case "trackContent": - trackContent(call); - break; - case "trackContentWithoutBuo": - trackContentWithoutBuo(call); - break; - case "setIdentity": - setIdentity(call); - break; - case "setRequestMetadata": - setRequestMetadata(call); - break; - case "logout": - logout(); - break; - case "getLatestReferringParams": - getLatestReferringParams(result); - break; - case "getFirstReferringParams": - getFirstReferringParams(result); - break; - case "setTrackingDisabled": - setTrackingDisabled(call); - break; - case "validateSDKIntegration": - validateSDKIntegration(); - break; - case "loadRewards": - loadRewards(call, result); - break; - case "redeemRewards": - redeemRewards(call, result); - break; - case "getCreditHistory": - getCreditHistory(call, result); - break; - case "isUserIdentified": - isUserIdentified(result); - break; - case "setConnectTimeout": - setConnectTimeout(call); - break; - case "setTimeout": - setTimeout(call); - break; - case "setRetryCount": - setRetryCount(call); - break; - case "setRetryInterval": - setRetryInterval(call); - break; - case "getLastAttributedTouchData": - getLastAttributedTouchData(call, result); - break; - default: - result.notImplemented(); - break; - } - } - - /** - * --------------------------------------------------------------------------------------------- - * Branch SDK Call Methods - * -------------------------------------------------------------------------------------------- - **/ - private final Branch.BranchReferralInitListener branchReferralInitListener = new - Branch.BranchReferralInitListener() { - @Override - public void onInitFinished(JSONObject params, BranchError error) { - if (error == null) { - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - params: " + params.toString()); - try { - initialParams = branchSdkHelper.paramsToMap(params); - } catch (JSONException e) { - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error to Map: " + e.getLocalizedMessage()); - return; - } - if (eventSink != null) { - eventSink.success(initialParams); - initialParams = null; - } - } else { - if (error.getErrorCode() == BranchError.ERR_BRANCH_ALREADY_INITIALIZED || error.getErrorCode() == BranchError.ERR_IMPROPER_REINITIALIZATION) { - return; - } - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error: " + error); - if (eventSink != null) { - eventSink.error(String.valueOf(error.getErrorCode()), error.getMessage(), null); - initialError = null; - } else { - initialError = error; - } - } - } - }; - - private void validateSDKIntegration() { - IntegrationValidator.validate(activity); - } - - private void getShortUrl(MethodCall call, final Result result) { - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - - HashMap argsMap = (HashMap) call.arguments; - BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); - - LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); - - final Map response = new HashMap<>(); - - buo.generateShortUrl(activity, linkProperties, new Branch.BranchLinkCreateListener() { - @Override - public void onLinkCreate(String url, BranchError error) { - - if (error == null) { - LogUtils.debug(DEBUG_NAME, "Branch link to share: " + url); - response.put("success", true); - response.put("url", url); - } else { - response.put("success", false); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } - - private void showShareSheet(MethodCall call, final Result result) { - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); - - LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); - String messageText = (String) argsMap.get("messageText"); - String messageTitle = (String) argsMap.get("messageTitle"); - String sharingTitle = (String) argsMap.get("sharingTitle"); - - final Map response = new HashMap<>(); - - ShareSheetStyle shareSheetStyle = new ShareSheetStyle(activity, messageTitle, messageText) - .setAsFullWidthStyle(true) - .setSharingTitle(sharingTitle); - - buo.showShareSheet(activity, - linkProperties, - shareSheetStyle, - new Branch.ExtendedBranchLinkShareListener() { - @Override - public void onShareLinkDialogLaunched() { - } - - @Override - public void onShareLinkDialogDismissed() { - } - - @Override - public void onLinkShareResponse(String sharedLink, String sharedChannel, BranchError error) { - if (error == null) { - LogUtils.debug(DEBUG_NAME, "Branch link share: " + sharedLink); - response.put("success", Boolean.TRUE); - response.put("url", sharedLink); - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - - @Override - public void onChannelSelected(String channelName) { - - } - - @Override - public boolean onChannelSelected(String channelName, BranchUniversalObject buo, LinkProperties linkProperties) { - return false; - } - }); - } - - private void registerView(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "registerView call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - final BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - buo.registerView(); - } - }); - } - - private void listOnSearch(MethodCall call, Result result) { - LogUtils.debug(DEBUG_NAME, "listOnSearch call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); - if (argsMap.containsKey("lp")) { - LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); - buo.listOnGoogleSearch(context, linkProperties); - } else { - buo.listOnGoogleSearch(context); - } - result.success(Boolean.TRUE); - } - - private void removeFromSearch(MethodCall call, Result result) { - LogUtils.debug(DEBUG_NAME, "removeFromSearch call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); - if (argsMap.containsKey("lp")) { - LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); - buo.removeFromLocalIndexing(context, linkProperties); - } else { - buo.removeFromLocalIndexing(context); - } - result.success(Boolean.TRUE); - } - - private void trackContent(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "trackContent call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - - final List buo = new ArrayList(); - for (HashMap b : (List>) argsMap.get("buo")) { - buo.add(branchSdkHelper.convertToBUO(b)); - } - final BranchEvent event = branchSdkHelper.convertToEvent((HashMap) argsMap.get("event")); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - event.addContentItems(buo).logEvent(context); - } - }); - } - - private void trackContentWithoutBuo(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "trackContentWithoutBuo call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - final BranchEvent event = branchSdkHelper.convertToEvent((HashMap) argsMap.get("event")); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - event.logEvent(context); - } - }); - } - - private void setIdentity(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setIdentity call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final String userId = call.argument("userId"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setIdentity(userId); - } - }); - } - - private void setRequestMetadata(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setRequestMetadata call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final String key = call.argument("key"); - final String value = call.argument("value"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setRequestMetadata(key, value); - } - }); - } - - private void logout() { - LogUtils.debug(DEBUG_NAME, "logout call"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).logout(); - } - }); - } - - private void getLatestReferringParams(Result result) { - LogUtils.debug(DEBUG_NAME, "getLatestReferringParams call"); - JSONObject sessionParams = Branch.getAutoInstance(context).getLatestReferringParams(); - try { - result.success(branchSdkHelper.paramsToMap(sessionParams)); - } catch (JSONException e) { - e.printStackTrace(); - result.error(DEBUG_NAME, e.getMessage(), null); - } - } - - private void getFirstReferringParams(Result result) { - LogUtils.debug(DEBUG_NAME, "getFirstReferringParams call"); - JSONObject sessionParams = Branch.getAutoInstance(context).getFirstReferringParams(); - try { - result.success(branchSdkHelper.paramsToMap(sessionParams)); - } catch (JSONException e) { - e.printStackTrace(); - result.error(DEBUG_NAME, e.getMessage(), null); - } - } - - private void setTrackingDisabled(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setTrackingDisabled call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final boolean value = call.argument("disable"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).disableTracking(value); - } - }); - } - - private void loadRewards(final MethodCall call, final Result result) { - LogUtils.debug(DEBUG_NAME, "loadRewards call"); - final Map response = new HashMap<>(); - Branch.getAutoInstance(context).loadRewards(new Branch.BranchReferralStateChangedListener() { - @Override - public void onStateChanged(boolean changed, @Nullable BranchError error) { - int credits; - if (error == null) { - if (!call.hasArgument("bucket")) { - credits = Branch.getAutoInstance(context).getCredits(); - } else { - credits = Branch.getAutoInstance(context).getCreditsForBucket(call.argument("bucket").toString()); - } - response.put("success", Boolean.TRUE); - response.put("credits", credits); - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } - - private void redeemRewards(final MethodCall call, final Result result) { - LogUtils.debug(DEBUG_NAME, "redeemRewards call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - - final int count = call.argument("count"); - final Map response = new HashMap<>(); - - if (!call.hasArgument("bucket")) { - Branch.getAutoInstance(context).redeemRewards(count, new Branch.BranchReferralStateChangedListener() { - @Override - public void onStateChanged(boolean changed, @Nullable BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } else { - Branch.getAutoInstance(context).redeemRewards(call.argument("bucket").toString(), count, new Branch.BranchReferralStateChangedListener() { - @Override - public void onStateChanged(boolean changed, @Nullable BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } - } - - private void getCreditHistory(final MethodCall call, final Result result) { - LogUtils.debug(DEBUG_NAME, "getCreditHistory call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final Map response = new HashMap<>(); - - if (!call.hasArgument("bucket")) { - Branch.getAutoInstance(context).getCreditHistory(new Branch.BranchListResponseListener() { - @Override - public void onReceivingResponse(JSONArray list, BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - JSONObject jo = new JSONObject(); - try { - jo.put("history", list); - response.put("data", branchSdkHelper.paramsToMap(jo)); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } else { - Branch.getAutoInstance(context).getCreditHistory(call.argument("bucket").toString(), new Branch.BranchListResponseListener() { - @Override - public void onReceivingResponse(JSONArray list, BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - JSONObject jo = new JSONObject(); - try { - jo.put("history", list); - response.put("data", branchSdkHelper.paramsToMap(jo)); - } catch (JSONException e) { - e.printStackTrace(); - } - - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } - } - - private void isUserIdentified(Result result) { - LogUtils.debug(DEBUG_NAME, "isUserIdentified call"); - result.success(Branch.getAutoInstance(context).isUserIdentified()); - } - - private void setConnectTimeout(final MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setConnectTimeout call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final int value = call.argument("connectTimeout"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setNetworkConnectTimeout(value); - } - }); - } - - private void setTimeout(final MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setConnectTimeout call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final int value = call.argument("timeout"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setNetworkTimeout(value); - } - }); - } - - private void setRetryCount(final MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setRetryCount call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final int value = call.argument("retryCount"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setRetryCount(value); - } - }); - } - - private void setRetryInterval(final MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setRetryInterval call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - final int value = call.argument("retryInterval"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setRetryInterval(value); - } - }); - } - - private void getLastAttributedTouchData(final MethodCall call, final Result result) { - LogUtils.debug(DEBUG_NAME, "getLastAttributedTouchData call"); - - final Map response = new HashMap<>(); - - if (call.hasArgument("attributionWindow")) { - final int attributionWindow = call.argument("attributionWindow"); - Branch.getAutoInstance(context).getLastAttributedTouchData( - new ServerRequestGetLATD.BranchLastAttributedTouchDataListener() { - @Override - public void onDataFetched(JSONObject jsonObject, BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - JSONObject jo = new JSONObject(); - try { - jo.put("latd", jsonObject); - response.put("data", branchSdkHelper.paramsToMap(jo)); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }, attributionWindow); - - } else { - Branch.getAutoInstance(context).getLastAttributedTouchData( - new ServerRequestGetLATD.BranchLastAttributedTouchDataListener() { - @Override - public void onDataFetched(JSONObject jsonObject, BranchError error) { - if (error == null) { - response.put("success", Boolean.TRUE); - JSONObject jo = new JSONObject(); - try { - jo.put("latd", jsonObject); - response.put("data", branchSdkHelper.paramsToMap(jo)); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); - response.put("errorMessage", error.getMessage()); - } - result.success(response); - } - }); - } - } - - // MethodChannel.Result wrapper that responds on the platform thread. - private static class MethodResultWrapper implements Result { - private final Result methodResult; - private final Handler handler; - - MethodResultWrapper(Result result) { - methodResult = result; - handler = new Handler(Looper.getMainLooper()); - } - - @Override - public void success(final Object result) { - handler.post( - new Runnable() { - @Override - public void run() { - try { - methodResult.success(result); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public void error( - final String errorCode, final String errorMessage, final Object errorDetails) { - handler.post( - new Runnable() { - @Override - public void run() { - try { - methodResult.error(errorCode, errorMessage, errorDetails); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public void notImplemented() { - handler.post( - new Runnable() { - @Override - public void run() { - try { - methodResult.notImplemented(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - } - - private static class MainThreadEventSink implements EventChannel.EventSink { - private final EventChannel.EventSink eventSink; - private final Handler handler; - - MainThreadEventSink(EventChannel.EventSink eventSink) { - this.eventSink = eventSink; - handler = new Handler(Looper.getMainLooper()); - } - - @Override - public void success(final Object o) { - handler.post(new Runnable() { - @Override - public void run() { - try { - if (eventSink != null) { - eventSink.success(o); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public void error(final String s, final String s1, final Object o) { - handler.post(new Runnable() { - @Override - public void run() { - try { - if (eventSink != null) { - eventSink.error(s, s1, o); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public void endOfStream() { - handler.post(new Runnable() { - @Override - public void run() { - try { - if (eventSink != null) { - eventSink.endOfStream(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - } -} - - +package br.com.rsmarques.flutter_branch_sdk; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.branch.indexing.BranchUniversalObject; +import io.branch.referral.Branch; +import io.branch.referral.BranchError; +import io.branch.referral.QRCode.BranchQRCode; +import io.branch.referral.ServerRequestGetLATD; +import io.branch.referral.util.BranchEvent; +import io.branch.referral.util.LinkProperties; +import io.branch.referral.util.ShareSheetStyle; +import io.branch.referral.validators.IntegrationValidator; +import io.flutter.embedding.android.FlutterFragmentActivity; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.EventChannel.StreamHandler; +import io.flutter.plugin.common.EventChannel.EventSink; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry.NewIntentListener; + +public class FlutterBranchSdkPlugin implements FlutterPlugin, MethodCallHandler, StreamHandler, NewIntentListener, ActivityAware, + Application.ActivityLifecycleCallbacks { + private static final String DEBUG_NAME = "FlutterBranchSDK"; + private Activity activity; + private Context context; + private ActivityPluginBinding activityPluginBinding; + + private static final String MESSAGE_CHANNEL = "flutter_branch_sdk/message"; + private static final String EVENT_CHANNEL = "flutter_branch_sdk/event"; + private EventSink eventSink = null; + private Map initialParams = null; + private BranchError initialError = null; + + private final FlutterBranchSdkHelper branchSdkHelper = new FlutterBranchSdkHelper(); + + /** + * --------------------------------------------------------------------------------------------- + * Plugin registry + * -------------------------------------------------------------------------------------------- + **/ + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + LogUtils.debug(DEBUG_NAME, "onAttachedToEngine call"); + setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext()); + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + LogUtils.debug(DEBUG_NAME, "onDetachedFromEngine call"); + teardownChannels(); + } + + private void setupChannels(BinaryMessenger messenger, Context context) { + LogUtils.debug(DEBUG_NAME, "setupChannels call"); + this.context = context; + + MethodChannel methodChannel = new MethodChannel(messenger, MESSAGE_CHANNEL); + EventChannel eventChannel = new EventChannel(messenger, EVENT_CHANNEL); + + methodChannel.setMethodCallHandler(this); + eventChannel.setStreamHandler(this); + + FlutterBranchSdkInit.init(context); + } + + private void setActivity(Activity activity) { + LogUtils.debug(DEBUG_NAME, "setActivity call"); + this.activity = activity; + activity.getApplication().registerActivityLifecycleCallbacks(this); + + if (this.activity != null && FlutterFragmentActivity.class.isAssignableFrom(activity.getClass())) { + Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent() != null ? activity.getIntent().getData() : null).init(); + } + } + + private void teardownChannels() { + LogUtils.debug(DEBUG_NAME, "teardownChannels call"); + this.activityPluginBinding = null; + this.activity = null; + this.context = null; + } + + /** + * --------------------------------------------------------------------------------------------- + * ActivityAware Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) { + LogUtils.debug(DEBUG_NAME, "onAttachedToActivity call"); + this.activityPluginBinding = activityPluginBinding; + setActivity(activityPluginBinding.getActivity()); + activityPluginBinding.addOnNewIntentListener(this); + } + + @Override + public void onDetachedFromActivity() { + LogUtils.debug(DEBUG_NAME, "onDetachedFromActivity call"); + activityPluginBinding.removeOnNewIntentListener(this); + this.activity = null; + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + LogUtils.debug(DEBUG_NAME, "onDetachedFromActivityForConfigChanges call"); + onDetachedFromActivity(); + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) { + LogUtils.debug(DEBUG_NAME, "onReattachedToActivityForConfigChanges call"); + onAttachedToActivity(activityPluginBinding); + } + + /** + * --------------------------------------------------------------------------------------------- + * StreamHandler Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onListen(Object o, EventChannel.EventSink eventSink) { + LogUtils.debug(DEBUG_NAME, "onListen call"); + this.eventSink = new MainThreadEventSink(eventSink); + if (initialParams != null) { + eventSink.success(initialParams); + initialParams = null; + initialError = null; + } else if (initialError != null) { + eventSink.error(String.valueOf(initialError.getErrorCode()), initialError.getMessage(), null); + initialParams = null; + initialError = null; + } + } + + @Override + public void onCancel(Object o) { + LogUtils.debug(DEBUG_NAME, "onCancel call"); + this.eventSink = new MainThreadEventSink(null); + initialError = null; + initialParams = null; + } + + /** + * --------------------------------------------------------------------------------------------- + * ActivityLifecycleCallbacks Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + } + + @Override + public void onActivityStarted(Activity activity) { + LogUtils.debug(DEBUG_NAME, "onActivityStarted call"); + Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent() != null ? activity.getIntent().getData() : null).init(); + } + + @Override + public void onActivityResumed(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + LogUtils.debug(DEBUG_NAME, "onActivityStopped call"); + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + LogUtils.debug(DEBUG_NAME, "onActivityDestroyed call"); + if (this.activity == activity) { + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + } + } + + /** + * --------------------------------------------------------------------------------------------- + * NewIntentListener Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public boolean onNewIntent(Intent intent) { + LogUtils.debug(DEBUG_NAME, "onNewIntent call"); + if (this.activity != null) { + this.activity.setIntent(intent); + + if (intent != null && + intent.hasExtra("branch_force_new_session") && + intent.getBooleanExtra("branch_force_new_session", false)) { + Branch.sessionBuilder(this.activity).withCallback(branchReferralInitListener).reInit(); + } + return true; + } + return false; + } + + /** + * --------------------------------------------------------------------------------------------- + * MethodCallHandler Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result rawResult) { + Result result = new MethodResultWrapper(rawResult); + switch (call.method) { + case "getShortUrl": + getShortUrl(call, result); + break; + case "showShareSheet": + showShareSheet(call, result); + break; + case "registerView": + registerView(call); + break; + case "listOnSearch": + listOnSearch(call, result); + break; + case "removeFromSearch": + removeFromSearch(call, result); + break; + case "trackContent": + trackContent(call); + break; + case "trackContentWithoutBuo": + trackContentWithoutBuo(call); + break; + case "setIdentity": + setIdentity(call); + break; + case "setRequestMetadata": + setRequestMetadata(call); + break; + case "logout": + logout(); + break; + case "getLatestReferringParams": + getLatestReferringParams(result); + break; + case "getFirstReferringParams": + getFirstReferringParams(result); + break; + case "setTrackingDisabled": + setTrackingDisabled(call); + break; + case "validateSDKIntegration": + validateSDKIntegration(); + break; + case "isUserIdentified": + isUserIdentified(result); + break; + case "setConnectTimeout": + setConnectTimeout(call); + break; + case "setTimeout": + setTimeout(call); + break; + case "setRetryCount": + setRetryCount(call); + break; + case "setRetryInterval": + setRetryInterval(call); + break; + case "getLastAttributedTouchData": + getLastAttributedTouchData(call, result); + break; + case "getQRCode": + getQRCode(call, result); + break; + default: + result.notImplemented(); + break; + } + } + + /** + * --------------------------------------------------------------------------------------------- + * Branch SDK Call Methods + * -------------------------------------------------------------------------------------------- + **/ + private final Branch.BranchReferralInitListener branchReferralInitListener = new + Branch.BranchReferralInitListener() { + @Override + public void onInitFinished(JSONObject params, BranchError error) { + if (error == null) { + LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - params: " + params.toString()); + try { + initialParams = branchSdkHelper.paramsToMap(params); + } catch (JSONException e) { + LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error to Map: " + e.getLocalizedMessage()); + return; + } + if (eventSink != null) { + eventSink.success(initialParams); + initialParams = null; + } + } else { + if (error.getErrorCode() == BranchError.ERR_BRANCH_ALREADY_INITIALIZED || error.getErrorCode() == BranchError.ERR_IMPROPER_REINITIALIZATION) { + return; + } + LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error: " + error); + if (eventSink != null) { + eventSink.error(String.valueOf(error.getErrorCode()), error.getMessage(), null); + initialError = null; + } else { + initialError = error; + } + } + } + }; + + private void validateSDKIntegration() { + IntegrationValidator.validate(activity); + } + + private void getShortUrl(MethodCall call, final Result result) { + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + + HashMap argsMap = (HashMap) call.arguments; + BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + + LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + + final Map response = new HashMap<>(); + + buo.generateShortUrl(activity, linkProperties, new Branch.BranchLinkCreateListener() { + @Override + public void onLinkCreate(String url, BranchError error) { + + if (error == null) { + LogUtils.debug(DEBUG_NAME, "Branch link to share: " + url); + response.put("success", true); + response.put("url", url); + } else { + response.put("success", false); + response.put("errorCode", String.valueOf(error.getErrorCode())); + response.put("errorMessage", error.getMessage()); + } + result.success(response); + } + }); + } + + private void showShareSheet(MethodCall call, final Result result) { + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + + LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + String messageText = (String) argsMap.get("messageText"); + String messageTitle = (String) argsMap.get("messageTitle"); + String sharingTitle = (String) argsMap.get("sharingTitle"); + + final Map response = new HashMap<>(); + + ShareSheetStyle shareSheetStyle = new ShareSheetStyle(activity, messageTitle, messageText) + .setAsFullWidthStyle(true) + .setSharingTitle(sharingTitle); + + buo.showShareSheet(activity, + linkProperties, + shareSheetStyle, + new Branch.ExtendedBranchLinkShareListener() { + @Override + public void onShareLinkDialogLaunched() { + } + + @Override + public void onShareLinkDialogDismissed() { + } + + @Override + public void onLinkShareResponse(String sharedLink, String sharedChannel, BranchError error) { + if (error == null) { + LogUtils.debug(DEBUG_NAME, "Branch link share: " + sharedLink); + response.put("success", Boolean.TRUE); + response.put("url", sharedLink); + } else { + response.put("success", Boolean.FALSE); + response.put("errorCode", String.valueOf(error.getErrorCode())); + response.put("errorMessage", error.getMessage()); + } + result.success(response); + } + + @Override + public void onChannelSelected(String channelName) { + + } + + @Override + public boolean onChannelSelected(String channelName, BranchUniversalObject buo, LinkProperties linkProperties) { + return false; + } + }); + } + + private void registerView(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "registerView call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + final BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + buo.registerView(); + } + }); + } + + private void listOnSearch(MethodCall call, Result result) { + LogUtils.debug(DEBUG_NAME, "listOnSearch call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + if (argsMap.containsKey("lp")) { + LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + buo.listOnGoogleSearch(context, linkProperties); + } else { + buo.listOnGoogleSearch(context); + } + result.success(Boolean.TRUE); + } + + private void removeFromSearch(MethodCall call, Result result) { + LogUtils.debug(DEBUG_NAME, "removeFromSearch call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + if (argsMap.containsKey("lp")) { + LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + buo.removeFromLocalIndexing(context, linkProperties); + } else { + buo.removeFromLocalIndexing(context); + } + result.success(Boolean.TRUE); + } + + private void trackContent(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "trackContent call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + + final List buo = new ArrayList(); + for (HashMap b : (List>) argsMap.get("buo")) { + buo.add(branchSdkHelper.convertToBUO(b)); + } + final BranchEvent event = branchSdkHelper.convertToEvent((HashMap) argsMap.get("event")); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + event.addContentItems(buo).logEvent(context); + } + }); + } + + private void trackContentWithoutBuo(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "trackContentWithoutBuo call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + final BranchEvent event = branchSdkHelper.convertToEvent((HashMap) argsMap.get("event")); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + event.logEvent(context); + } + }); + } + + private void setIdentity(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setIdentity call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String userId = call.argument("userId"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setIdentity(userId); + } + }); + } + + private void setRequestMetadata(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setRequestMetadata call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String key = call.argument("key"); + final String value = call.argument("value"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setRequestMetadata(key, value); + } + }); + } + + private void logout() { + LogUtils.debug(DEBUG_NAME, "logout call"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).logout(); + } + }); + } + + private void getLatestReferringParams(Result result) { + LogUtils.debug(DEBUG_NAME, "getLatestReferringParams call"); + JSONObject sessionParams = Branch.getAutoInstance(context).getLatestReferringParams(); + try { + result.success(branchSdkHelper.paramsToMap(sessionParams)); + } catch (JSONException e) { + e.printStackTrace(); + result.error(DEBUG_NAME, e.getMessage(), null); + } + } + + private void getFirstReferringParams(Result result) { + LogUtils.debug(DEBUG_NAME, "getFirstReferringParams call"); + JSONObject sessionParams = Branch.getAutoInstance(context).getFirstReferringParams(); + try { + result.success(branchSdkHelper.paramsToMap(sessionParams)); + } catch (JSONException e) { + e.printStackTrace(); + result.error(DEBUG_NAME, e.getMessage(), null); + } + } + + private void setTrackingDisabled(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setTrackingDisabled call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final boolean value = call.argument("disable"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).disableTracking(value); + } + }); + } + + private void isUserIdentified(Result result) { + LogUtils.debug(DEBUG_NAME, "isUserIdentified call"); + result.success(Branch.getAutoInstance(context).isUserIdentified()); + } + + private void setConnectTimeout(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setConnectTimeout call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final int value = call.argument("connectTimeout"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setNetworkConnectTimeout(value); + } + }); + } + + private void setTimeout(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setConnectTimeout call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final int value = call.argument("timeout"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setNetworkTimeout(value); + } + }); + } + + private void setRetryCount(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setRetryCount call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final int value = call.argument("retryCount"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setRetryCount(value); + } + }); + } + + private void setRetryInterval(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "setRetryInterval call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final int value = call.argument("retryInterval"); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setRetryInterval(value); + } + }); + } + + private void getLastAttributedTouchData(final MethodCall call, final Result result) { + LogUtils.debug(DEBUG_NAME, "getLastAttributedTouchData call"); + + final Map response = new HashMap<>(); + + if (call.hasArgument("attributionWindow")) { + final int attributionWindow = call.argument("attributionWindow"); + Branch.getAutoInstance(context).getLastAttributedTouchData( + new ServerRequestGetLATD.BranchLastAttributedTouchDataListener() { + @Override + public void onDataFetched(JSONObject jsonObject, BranchError error) { + if (error == null) { + response.put("success", Boolean.TRUE); + JSONObject jo = new JSONObject(); + try { + jo.put("latd", jsonObject); + response.put("data", branchSdkHelper.paramsToMap(jo)); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + response.put("success", Boolean.FALSE); + response.put("errorCode", String.valueOf(error.getErrorCode())); + response.put("errorMessage", error.getMessage()); + } + result.success(response); + } + }, attributionWindow); + + } else { + Branch.getAutoInstance(context).getLastAttributedTouchData( + new ServerRequestGetLATD.BranchLastAttributedTouchDataListener() { + @Override + public void onDataFetched(JSONObject jsonObject, BranchError error) { + if (error == null) { + response.put("success", Boolean.TRUE); + JSONObject jo = new JSONObject(); + try { + jo.put("latd", jsonObject); + response.put("data", branchSdkHelper.paramsToMap(jo)); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + response.put("success", Boolean.FALSE); + response.put("errorCode", String.valueOf(error.getErrorCode())); + response.put("errorMessage", error.getMessage()); + } + result.success(response); + } + }); + } + } + + private void getQRCode(final MethodCall call, final Result result) { + + LogUtils.debug(DEBUG_NAME, "getQRCodeAsData call"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + HashMap argsMap = (HashMap) call.arguments; + + final BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + final LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + final BranchQRCode branchQRCode = branchSdkHelper.convertToQRCode((HashMap) argsMap.get("qrCodeSettings")); + final Map response = new HashMap<>(); + + + try { + branchQRCode.getQRCodeAsData(context, buo, linkProperties, new BranchQRCode.BranchQRCodeDataHandler() { + @Override + public void onSuccess(byte[] qrCodeData) { + + response.put("success", Boolean.TRUE); + response.put("result", qrCodeData); + result.success(response); + } + @Override + public void onFailure(Exception error) { + response.put("success", Boolean.FALSE); + response.put("errorCode", "-1"); + response.put("errorMessage", error.getMessage()); + result.success(response); + } + }); + } catch (IOException e) { + response.put("success", Boolean.FALSE); + response.put("errorCode", "-1"); + response.put("errorMessage", e.getMessage()); + result.success(response); + } + } +} + + diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/LogUtils.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/LogUtils.java index bd037a90..d47a7e10 100644 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/LogUtils.java +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/LogUtils.java @@ -1,11 +1,13 @@ -package br.com.rsmarques.flutter_branch_sdk; - -import android.util.Log; - -public class LogUtils { - public static void debug(final String tag, String message) { - if (BuildConfig.DEBUG) { - Log.d(tag, message); - } - } -} +package br.com.rsmarques.flutter_branch_sdk; + +import android.util.Log; + +import io.flutter.BuildConfig; + +public class LogUtils { + public static void debug(final String tag, String message) { + if (BuildConfig.DEBUG) { + Log.d(tag, message); + } + } +} diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MainThreadEventSink.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MainThreadEventSink.java new file mode 100644 index 00000000..2709b401 --- /dev/null +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MainThreadEventSink.java @@ -0,0 +1,64 @@ +package br.com.rsmarques.flutter_branch_sdk; + +import android.os.Handler; +import android.os.Looper; + +import io.flutter.plugin.common.EventChannel; + +public class MainThreadEventSink implements EventChannel.EventSink { + private final EventChannel.EventSink eventSink; + private final Handler handler; + + MainThreadEventSink(EventChannel.EventSink eventSink) { + this.eventSink = eventSink; + handler = new Handler(Looper.getMainLooper()); + } + + @Override + public void success(final Object o) { + handler.post(new Runnable() { + @Override + public void run() { + try { + if (eventSink != null) { + eventSink.success(o); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void error(final String s, final String s1, final Object o) { + handler.post(new Runnable() { + @Override + public void run() { + try { + if (eventSink != null) { + eventSink.error(s, s1, o); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void endOfStream() { + handler.post(new Runnable() { + @Override + public void run() { + try { + if (eventSink != null) { + eventSink.endOfStream(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } +} diff --git a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MethodResultWrapper.java b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MethodResultWrapper.java new file mode 100644 index 00000000..3bc513ce --- /dev/null +++ b/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/MethodResultWrapper.java @@ -0,0 +1,63 @@ +package br.com.rsmarques.flutter_branch_sdk; + +import android.os.Handler; +import android.os.Looper; + +import io.flutter.plugin.common.MethodChannel; + +// MethodChannel.Result wrapper that responds on the platform thread. +public class MethodResultWrapper implements MethodChannel.Result { + private final MethodChannel.Result methodResult; + private final Handler handler; + + MethodResultWrapper(MethodChannel.Result result) { + methodResult = result; + handler = new Handler(Looper.getMainLooper()); + } + + @Override + public void success(final Object result) { + handler.post( + new Runnable() { + @Override + public void run() { + try { + methodResult.success(result); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void error( + final String errorCode, final String errorMessage, final Object errorDetails) { + handler.post( + new Runnable() { + @Override + public void run() { + try { + methodResult.error(errorCode, errorMessage, errorDetails); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void notImplemented() { + handler.post( + new Runnable() { + @Override + public void run() { + try { + methodResult.notImplemented(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } +} diff --git a/assets/branch.png b/assets/branch.png new file mode 100644 index 00000000..7ae2d2f5 Binary files /dev/null and b/assets/branch.png differ diff --git a/assets/branch_logo_qrcode.jpeg b/assets/branch_logo_qrcode.jpeg new file mode 100644 index 00000000..14e6d995 Binary files /dev/null and b/assets/branch_logo_qrcode.jpeg differ diff --git a/assets/example.png b/assets/example.png index 48c87736..3f299892 100644 Binary files a/assets/example.png and b/assets/example.png differ diff --git a/example/.gitignore b/example/.gitignore index ae1f1838..504d1e56 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,37 +1,47 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index 1b5cec02..00000000 --- a/example/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: 27321ebbad34b0a3fafe99fac037102196d655ff - channel: stable - -project_type: app diff --git a/example/README.md b/example/README.md index 94c1b38d..293d9766 100644 --- a/example/README.md +++ b/example/README.md @@ -1,9 +1,9 @@ -# flutter_branch_sdk_example - -Demonstrates how to use the flutter_branch_sdk plugin. - -## Getting Started - +# flutter_branch_sdk_example + +Demonstrates how to use the flutter_branch_sdk plugin. + +## Getting Started + See the `example` directory for a complete sample app using Branch SDK. -![Example app](https://user-images.githubusercontent.com/17687286/70445281-0b87c180-1a7a-11ea-8611-7217d46c75a7.png) +![Example app](https://user-images.githubusercontent.com/17687286/70445281-0b87c180-1a7a-11ea-8611-7217d46c75a7.png) \ No newline at end of file diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 00000000..b4ce1be2 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore index bc2100d8..5d99765d 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -1,7 +1,13 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 312b0e55..17024421 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,65 +1,63 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 31 - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "br.com.rsmarques.flutter_branch_sdk_example" - minSdkVersion 21 - targetSdkVersion 31 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - multiDexEnabled true - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - shrinkResources true - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "br.com.rsmarques.flutter_branch_sdk_example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion 21 + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + multiDexEnabled true + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + shrinkResources true + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +flutter { + source '../..' +} diff --git a/example/android/app/proguard-rules.pro b/example/android/app/proguard-rules.pro old mode 100755 new mode 100644 index be37f52e..24f63cf8 --- a/example/android/app/proguard-rules.pro +++ b/example/android/app/proguard-rules.pro @@ -1,16 +1,16 @@ -#Flutter Wrapper --keep class io.flutter.app.** { *; } --keep class io.flutter.plugin.** { *; } --keep class io.flutter.util.** { *; } --keep class io.flutter.view.** { *; } --keep class io.flutter.** { *; } --keep class io.flutter.plugins.** { *; } --keep class com.google.android.gms.ads.identifier.** { *; } --keep class com.google.android.gms.* {*;} --keep class com.google.android.gms.ads.identifier.AdvertisingIdClient { - com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context); -} --keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info { - java.lang.String getId(); - boolean isLimitAdTrackingEnabled(); +#Flutter Wrapper +-keep class io.flutter.app.** { *; } +-keep class io.flutter.plugin.** { *; } +-keep class io.flutter.util.** { *; } +-keep class io.flutter.view.** { *; } +-keep class io.flutter.** { *; } +-keep class io.flutter.plugins.** { *; } +-keep class com.google.android.gms.ads.identifier.** { *; } +-keep class com.google.android.gms.* {*;} +-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient { + com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context); +} +-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info { + java.lang.String getId(); + boolean isLimitAdTrackingEnabled(); } \ No newline at end of file diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index 5e6de6c3..3cc9bbcf 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,7 +1,8 @@ - - - - + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index cc885d41..f935ae78 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,61 +1,65 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/java/br/com/rsmarques/flutter_branch_sdk_example/MainActivity.java b/example/android/app/src/main/java/br/com/rsmarques/flutter_branch_sdk_example/MainActivity.java index b6a37ecb..e2b26366 100644 --- a/example/android/app/src/main/java/br/com/rsmarques/flutter_branch_sdk_example/MainActivity.java +++ b/example/android/app/src/main/java/br/com/rsmarques/flutter_branch_sdk_example/MainActivity.java @@ -1,7 +1,6 @@ -package br.com.rsmarques.flutter_branch_sdk_example; - -import io.flutter.embedding.android.FlutterActivity; - -public class MainActivity extends FlutterActivity { - -} +package br.com.rsmarques.flutter_branch_sdk_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..1cb7aa2f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml index 304732f8..84037589 100644 --- a/example/android/app/src/main/res/drawable/launch_background.xml +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,12 @@ - - - - - - - - + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..360a1605 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa4417..5fac6796 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ - - - - + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index 5e6de6c3..3cc9bbcf 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,7 +1,8 @@ - - - - + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index 0b4cf534..104a4864 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,29 +1,31 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a3..46c1f169 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 939efa29..258d5e1f 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14fb..33f0745d 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,11 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/android/settings_aar.gradle b/example/android/settings_aar.gradle deleted file mode 100644 index e7b4def4..00000000 --- a/example/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/example/assets/images/branch_logo.jpeg b/example/assets/images/branch_logo.jpeg new file mode 100644 index 00000000..14e6d995 Binary files /dev/null and b/example/assets/images/branch_logo.jpeg differ diff --git a/example/ios/.gitignore b/example/ios/.gitignore index e96ef602..ad322bc0 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,32 +1,34 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id deleted file mode 100644 index a8c8b3dd..00000000 --- a/example/ios/Flutter/.last_build_id +++ /dev/null @@ -1 +0,0 @@ -c7efa40e8c2c02006bfd59baf861b387 \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f2872cf4..80391e41 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -1,26 +1,26 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 9.0 - - + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index e8efba11..dfd26268 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index 399e9340..a97381ae 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 6034cd70..b7fcc920 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,8 +1,8 @@ PODS: - - Branch (1.42.0) + - Branch (1.43.1) - Flutter (1.0.0) - flutter_branch_sdk (3.0.0): - - Branch (~> 1.42.0) + - Branch (~> 1.43.0) - Flutter DEPENDENCIES: @@ -20,9 +20,9 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_branch_sdk/ios" SPEC CHECKSUMS: - Branch: a6f1d597dc7c027360f386d05e8d109043b207d8 + Branch: b5b57fc2e6f098916fd2ea26c9b66f52ffe7e293 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - flutter_branch_sdk: ecb5dd985a092ef5aecc9195c6645649e6ea1b9f + flutter_branch_sdk: dcf38505c8dcb3249841e2acaf323f4a39f30e2b PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index fd09e50a..15c0cdc0 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,15 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 5B1C365AF4DCE9C8CC3B7F6D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9EBDF3395510D4A330D468B /* Pods_Runner.framework */; }; - 5B6F7EDD23B72088001411F0 /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B6F7EDC23B72088001411F0 /* SafariServices.framework */; }; - 5BDFC8F823A9F2D000352C5A /* Runner.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 5BDFC8F723A9F28700352C5A /* Runner.entitlements */; }; + 530C6B849848A7137517F90B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5955988AC5EF176002150C7E /* Pods_Runner.framework */; }; + 5B86E4EB285AF073001770A9 /* Runner.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 5B86E4EA285AF021001770A9 /* Runner.entitlements */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -32,17 +31,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 124E06BB8FCD54487179B61F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5B6F7EDC23B72088001411F0 /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = System/Library/Frameworks/SafariServices.framework; sourceTree = SDKROOT; }; - 5BDFC8F723A9F28700352C5A /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 5955988AC5EF176002150C7E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5B86E4EA285AF021001770A9 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 76B772D28CA1719E9F34FD8C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 7D4B4F1BEF1D1B627C15D066 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 946D0500AB85035C69611626 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 896072A09BADE8B62197469F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -50,7 +48,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A9EBDF3395510D4A330D468B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A35C6EF0AAAF92EE49DA1FCA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,29 +56,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5B6F7EDD23B72088001411F0 /* SafariServices.framework in Frameworks */, - 5B1C365AF4DCE9C8CC3B7F6D /* Pods_Runner.framework in Frameworks */, + 530C6B849848A7137517F90B /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 28F1E736C41452F4D999097B /* Frameworks */ = { + 4B08117C6FAFAE2CA00517DF /* Frameworks */ = { isa = PBXGroup; children = ( - 5B6F7EDC23B72088001411F0 /* SafariServices.framework */, - A9EBDF3395510D4A330D468B /* Pods_Runner.framework */, + 5955988AC5EF176002150C7E /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; - 648E178D9C596B1BBB8D9F3B /* Pods */ = { + 83204363E71EE2485FF0D939 /* Pods */ = { isa = PBXGroup; children = ( - 946D0500AB85035C69611626 /* Pods-Runner.debug.xcconfig */, - 7D4B4F1BEF1D1B627C15D066 /* Pods-Runner.release.xcconfig */, - 76B772D28CA1719E9F34FD8C /* Pods-Runner.profile.xcconfig */, + 124E06BB8FCD54487179B61F /* Pods-Runner.debug.xcconfig */, + A35C6EF0AAAF92EE49DA1FCA /* Pods-Runner.release.xcconfig */, + 896072A09BADE8B62197469F /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -102,8 +98,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 648E178D9C596B1BBB8D9F3B /* Pods */, - 28F1E736C41452F4D999097B /* Frameworks */, + 83204363E71EE2485FF0D939 /* Pods */, + 4B08117C6FAFAE2CA00517DF /* Frameworks */, ); sourceTree = ""; }; @@ -118,12 +114,11 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 5BDFC8F723A9F28700352C5A /* Runner.entitlements */, + 5B86E4EA285AF021001770A9 /* Runner.entitlements */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -132,13 +127,6 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -146,14 +134,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 369621B7B15D68E43DA939A7 /* [CP] Check Pods Manifest.lock */, + 51684BB5E9E4DDF572E3CBA1 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 8E96BCAB8DDECFEFCEBC5B91 /* [CP] Embed Pods Frameworks */, + F2E1F5326562B92A6334FD21 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -171,17 +159,16 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 1300; - ORGANIZATIONNAME = "The Chromium Authors"; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = XM2A23Q5KM; LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -203,7 +190,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5BDFC8F823A9F2D000352C5A /* Runner.entitlements in Resources */, + 5B86E4EB285AF073001770A9 /* Runner.entitlements in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, @@ -214,7 +201,21 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 369621B7B15D68E43DA939A7 /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 51684BB5E9E4DDF572E3CBA1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -236,54 +237,37 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Thin Binary"; + name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - 8E96BCAB8DDECFEFCEBC5B91 /* [CP] Embed Pods Frameworks */ = { + F2E1F5326562B92A6334FD21 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Branch/Branch.framework", - "${BUILT_PRODUCTS_DIR}/flutter_branch_sdk/flutter_branch_sdk.framework", + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Branch.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_branch_sdk.framework", + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -378,39 +362,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = XM2A23Q5KM; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - OTHER_LDFLAGS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "-framework", - "\"AdSupport\"", - "-framework", - "\"Branch\"", - "-framework", - "\"CoreTelephony\"", - "-framework", - "\"Flutter\"", - "-framework", - "\"MobileCoreServices\"", - "-framework", - "\"SystemConfiguration\"", - "-framework", - "\"WebKit\"", - "-framework", - "\"flutter_branch_sdk\"", - "-framework", - "\"iAd\"", - "-framework", - "\"Flutter\"", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "br.com.rsmarques.flutter-branch-sdk-example"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -520,7 +475,8 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -536,39 +492,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = XM2A23Q5KM; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-framework", - "\"AdSupport\"", - "-framework", - "\"Branch\"", - "-framework", - "\"CoreTelephony\"", - "-framework", - "\"Flutter\"", - "-framework", - "\"MobileCoreServices\"", - "-framework", - "\"SystemConfiguration\"", - "-framework", - "\"WebKit\"", - "-framework", - "\"flutter_branch_sdk\"", - "-framework", - "\"iAd\"", - "-framework", - "\"Flutter\"", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "br.com.rsmarques.flutter-branch-sdk-example"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -589,39 +516,10 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = XM2A23Q5KM; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); - OTHER_LDFLAGS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "-framework", - "\"AdSupport\"", - "-framework", - "\"Branch\"", - "-framework", - "\"CoreTelephony\"", - "-framework", - "\"Flutter\"", - "-framework", - "\"MobileCoreServices\"", - "-framework", - "\"SystemConfiguration\"", - "-framework", - "\"WebKit\"", - "-framework", - "\"flutter_branch_sdk\"", - "-framework", - "\"iAd\"", - "-framework", - "\"Flutter\"", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = "br.com.rsmarques.flutter-branch-sdk-example"; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 919434a6..c4b79bd8 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..fc6bf807 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..af0309c4 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a3..f9cbb254 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,87 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index 18d98100..fc6bf807 100644 --- a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -1,8 +1,8 @@ - - - - - IDEDidComputeMac32BitWarning - - - + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..af0309c4 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4a..37636837 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,13 +1,13 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json index d36b1fab..1950fd80 100644 --- a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,122 +1,122 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index 0bedcf2f..d08a4de3 100644 --- a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index 89c2725b..65a94b5d 100644 --- a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -1,5 +1,5 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard index f2e259c7..497371ea 100644 --- a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard index f3c28516..bbb83caa 100644 --- a/example/ios/Runner/Base.lproj/Main.storyboard +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index e78dfcbd..f986b2c3 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,8 +2,12 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Flutter Branch Sdk CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -11,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Flutter Branch SDK Example + flutter_branch_sdk_example CFBundlePackageType APPL CFBundleShortVersionString @@ -31,6 +35,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSUserTrackingUsageDescription + App would like to access IDFA for tracking purpose UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -56,15 +62,11 @@ branch_check_pasteboard - branch_key - key_test_ipQTteg11ENANDeCzSXgqdgfuycWoXYH - NSUserTrackingUsageDescription - App would like to access IDFA for tracking purpose - branch_enable_log - branch_enable_facebook_ads - CADisableMinimumFrameDurationOnPhone + branch_enable_log + branch_key + key_test_ipQTteg11ENANDeCzSXgqdgfuycWoXYH diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h index 7335fdf9..fae207f9 100644 --- a/example/ios/Runner/Runner-Bridging-Header.h +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -1 +1 @@ -#import "GeneratedPluginRegistrant.h" \ No newline at end of file +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/Runner/Runner.entitlements b/example/ios/Runner/Runner.entitlements index 5731f420..f03a42e3 100644 --- a/example/ios/Runner/Runner.entitlements +++ b/example/ios/Runner/Runner.entitlements @@ -2,12 +2,12 @@ - com.apple.developer.associated-domains - - applinks:flutterbranchsdk.app.link - applinks:flutterbranchsdk-alternate.app.link + com.apple.developer.associated-domains + + applinks:flutterbranchsdk.app.link + applinks:flutterbranchsdk-alternate.app.link applinks:flutterbranchsdk.test-app.link applinks:flutterbranchsdk-alternate.test-app.link - + diff --git a/example/lib/custom_button.dart b/example/lib/custom_button.dart index 809da8db..cee297ac 100644 --- a/example/lib/custom_button.dart +++ b/example/lib/custom_button.dart @@ -1,19 +1,20 @@ -import 'package:flutter/material.dart'; - -class CustomButton extends StatelessWidget { - CustomButton({required this.onPressed, required this.child}); - - final GestureTapCallback onPressed; - final Widget child; - - @override - Widget build(BuildContext context) { - return Container( - height: 50, - padding: EdgeInsets.symmetric(vertical: 4, horizontal: 2), - child: ElevatedButton( - child: child, - onPressed: onPressed, - )); - } -} +import 'package:flutter/material.dart'; + +class CustomButton extends StatelessWidget { + const CustomButton({Key? key, required this.onPressed, required this.child}) + : super(key: key); + + final GestureTapCallback onPressed; + final Widget child; + + @override + Widget build(BuildContext context) { + return Container( + height: 50, + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 2), + child: ElevatedButton( + onPressed: onPressed, + child: child, + )); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 4e054f3a..ccfeb5db 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,574 +1,707 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_branch_sdk/flutter_branch_sdk.dart'; - -import 'custom_button.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - Widget build(BuildContext context) { - return MaterialApp( - title: "Flutter Branch SDK Example", - debugShowCheckedModeBanner: false, - home: HomePage(), - ); - } -} - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - final GlobalKey scaffoldMessengerKey = - GlobalKey(); - - BranchContentMetaData metadata = BranchContentMetaData(); - BranchUniversalObject? buo; - BranchLinkProperties lp = BranchLinkProperties(); - BranchEvent? eventStandart; - BranchEvent? eventCustom; - - StreamSubscription? streamSubscription; - StreamController controllerData = StreamController(); - StreamController controllerInitSession = StreamController(); - StreamController controllerUrl = StreamController(); - - @override - void initState() { - super.initState(); - - listenDynamicLinks(); - - initDeepLinkData(); - - FlutterBranchSdk.setIdentity('branch_user_test'); - - //requestATTTracking(); - } - - void requestATTTracking() async { - AppTrackingStatus status; - status = await FlutterBranchSdk.requestTrackingAuthorization(); - print(status); - - status = await FlutterBranchSdk.getTrackingAuthorizationStatus(); - print(status); - - final uuid = await FlutterBranchSdk.getAdvertisingIdentifier(); - print(uuid); - } - - void listenDynamicLinks() async { - streamSubscription = FlutterBranchSdk.initSession().listen((data) { - print('listenDynamicLinks - DeepLink Data: $data'); - controllerData.sink.add((data.toString())); - if (data.containsKey('+clicked_branch_link') && - data['+clicked_branch_link'] == true) { - print( - '------------------------------------Link clicked----------------------------------------------'); - print('Custom string: ${data['custom_string']}'); - print('Custom number: ${data['custom_number']}'); - print('Custom bool: ${data['custom_bool']}'); - print('Custom list number: ${data['custom_list_number']}'); - print( - '------------------------------------------------------------------------------------------------'); - showSnackBar( - context: context, - message: 'Link clicked: Custom string - ${data['custom_string']}', - duration: 10); - } - }, onError: (error) { - PlatformException platformException = error as PlatformException; - print( - 'InitSession error: ${platformException.code} - ${platformException.message}'); - controllerInitSession.add( - 'InitSession error: ${platformException.code} - ${platformException.message}'); - }); - } - - void initDeepLinkData() { - metadata = BranchContentMetaData() - ..addCustomMetadata('custom_string', 'abc') - ..addCustomMetadata('custom_number', 12345) - ..addCustomMetadata('custom_bool', true) - ..addCustomMetadata('custom_list_number', [1, 2, 3, 4, 5]) - ..addCustomMetadata('custom_list_string', ['a', 'b', 'c']) - //--optional Custom Metadata - ..contentSchema = BranchContentSchema.COMMERCE_PRODUCT - ..price = 50.99 - ..currencyType = BranchCurrencyType.BRL - ..quantity = 50 - ..sku = 'sku' - ..productName = 'productName' - ..productBrand = 'productBrand' - ..productCategory = BranchProductCategory.ELECTRONICS - ..productVariant = 'productVariant' - ..condition = BranchCondition.NEW - ..rating = 100 - ..ratingAverage = 50 - ..ratingMax = 100 - ..ratingCount = 2 - ..setAddress( - street: 'street', - city: 'city', - region: 'ES', - country: 'Brazil', - postalCode: '99999-987') - ..setLocation(31.4521685, -114.7352207); - - buo = BranchUniversalObject( - canonicalIdentifier: 'flutter/branch', - //parameter canonicalUrl - //If your content lives both on the web and in the app, make sure you set its canonical URL - // (i.e. the URL of this piece of content on the web) when building any BUO. - // By doing so, we’ll attribute clicks on the links that you generate back to their original web page, - // even if the user goes to the app instead of your website! This will help your SEO efforts. - canonicalUrl: 'https://flutter.dev', - title: 'Flutter Branch Plugin', - imageUrl: - 'https://flutter.dev/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg', - contentDescription: 'Flutter Branch Description', - /* - contentMetadata: BranchContentMetaData() - ..addCustomMetadata('custom_string', 'abc') - ..addCustomMetadata('custom_number', 12345) - ..addCustomMetadata('custom_bool', true) - ..addCustomMetadata('custom_list_number', [1, 2, 3, 4, 5]) - ..addCustomMetadata('custom_list_string', ['a', 'b', 'c']), - */ - contentMetadata: metadata, - keywords: ['Plugin', 'Branch', 'Flutter'], - publiclyIndex: true, - locallyIndex: true, - expirationDateInMilliSec: - DateTime.now().add(Duration(days: 365)).millisecondsSinceEpoch); - - lp = BranchLinkProperties( - channel: 'facebook', - feature: 'sharing', - //parameter alias - //Instead of our standard encoded short url, you can specify the vanity alias. - // For example, instead of a random string of characters/integers, you can set the vanity alias as *.app.link/devonaustin. - // Aliases are enforced to be unique** and immutable per domain, and per link - they cannot be reused unless deleted. - //alias: 'https://branch.io' //define link url, - stage: 'new share', - campaign: 'xxxxx', - tags: ['one', 'two', 'three']) - ..addControlParam('\$uri_redirect_mode', '1') - ..addControlParam('referring_user_id', 'asdf'); - - eventStandart = BranchEvent.standardEvent(BranchStandardEvent.ADD_TO_CART) - //--optional Event data - ..transactionID = '12344555' - ..currency = BranchCurrencyType.BRL - ..revenue = 1.5 - ..shipping = 10.2 - ..tax = 12.3 - ..coupon = 'test_coupon' - ..affiliation = 'test_affiliation' - ..eventDescription = 'Event_description' - ..searchQuery = 'item 123' - ..adType = BranchEventAdType.BANNER - ..addCustomData( - 'Custom_Event_Property_Key1', 'Custom_Event_Property_val1') - ..addCustomData( - 'Custom_Event_Property_Key2', 'Custom_Event_Property_val2'); - - eventCustom = BranchEvent.customEvent('Custom_event') - ..addCustomData( - 'Custom_Event_Property_Key1', 'Custom_Event_Property_val1') - ..addCustomData( - 'Custom_Event_Property_Key2', 'Custom_Event_Property_val2'); - } - - void showSnackBar( - {required BuildContext context, - required String message, - int duration = 1}) { - scaffoldMessengerKey.currentState!.removeCurrentSnackBar(); - scaffoldMessengerKey.currentState!.showSnackBar( - SnackBar( - content: Text(message), - duration: Duration(seconds: duration), - ), - ); - } - - @override - Widget build(BuildContext context) { - return ScaffoldMessenger( - key: scaffoldMessengerKey, - child: Scaffold( - appBar: AppBar( - title: const Text('Branch.io Plugin Example App'), - ), - body: Scrollbar( - isAlwaysShown: true, - child: SingleChildScrollView( - padding: EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - StreamBuilder( - stream: controllerInitSession.stream, - initialData: '', - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data!.isNotEmpty) { - return Column( - children: [ - Center( - child: Text( - snapshot.data!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.red), - )) - ], - ); - } else { - return Container(); - } - }, - ), - CustomButton( - child: Text('Validate SDK Integration'), - onPressed: () { - if (kIsWeb) { - showSnackBar( - context: context, - message: - 'validateSDKIntegration() not available in Flutter Web'); - return; - } - - FlutterBranchSdk.validateSDKIntegration(); - if (Platform.isAndroid) { - showSnackBar( - context: context, - message: 'Check messages in run log or logcat'); - } - }, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: CustomButton( - child: Text('Enable tracking'), - onPressed: () { - FlutterBranchSdk.disableTracking(false); - showSnackBar( - context: context, message: 'Tracking enabled'); - }, - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: CustomButton( - child: Text('Disable tracking'), - onPressed: () { - FlutterBranchSdk.disableTracking(true); - showSnackBar( - context: context, message: 'Tracking disabled'); - }, - ), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: CustomButton( - child: Text('Identify user'), - onPressed: () { - FlutterBranchSdk.setIdentity('branch_user_test'); - showSnackBar( - context: context, - message: 'User branch_user_test identfied'); - }, - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: CustomButton( - child: Text('User logout'), - onPressed: () { - FlutterBranchSdk.logout(); - showSnackBar( - context: context, - message: 'User branch_user_test logout'); - }, - ), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: CustomButton( - child: Text('Register view'), - onPressed: () { - FlutterBranchSdk.registerView(buo: buo!); - - showSnackBar( - context: context, message: 'Event Registered'); - }, - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: CustomButton( - child: Text('Track content'), - onPressed: () { - //FlutterBranchSdk.trackContent( - // buo: [buo!], branchEvent: eventStandart!); - - FlutterBranchSdk.trackContent( - buo: [buo!], branchEvent: eventCustom!); - /* - FlutterBranchSdk.trackContentWithoutBuo( - branchEvent: eventStandart!); - FlutterBranchSdk.trackContentWithoutBuo( - branchEvent: eventCustom!); - */ - showSnackBar( - context: context, message: 'Tracked content'); - }, - ), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: CustomButton( - child: Text('Get First Parameters'), - onPressed: () async { - Map params = - await FlutterBranchSdk.getFirstReferringParams(); - controllerData.sink.add(params.toString()); - showSnackBar( - context: context, - message: 'First Parameters recovered'); - }, - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: CustomButton( - child: Text('Get Last Parameters'), - onPressed: () async { - Map params = - await FlutterBranchSdk.getLatestReferringParams(); - controllerData.sink.add(params.toString()); - showSnackBar( - context: context, - message: 'Last Parameters recovered'); - }, - ), - ), - ], - ), - CustomButton( - child: Text('Get last Attributed'), - onPressed: getLastAttributed, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Expanded( - child: CustomButton( - child: Text('List on Search'), - onPressed: () async { - if (kIsWeb) { - showSnackBar( - context: context, - message: - 'listOnSearch() not available in Flutter Web'); - return; - } - bool success = - await FlutterBranchSdk.listOnSearch(buo: buo!); - - success = await FlutterBranchSdk.listOnSearch( - buo: buo!, linkProperties: lp); - - if (success) { - showSnackBar( - context: context, message: 'Listed on Search'); - } - }, - ), - ), - SizedBox( - width: 10, - ), - Expanded( - child: CustomButton( - child: Text('Remove from Search'), - onPressed: () async { - if (kIsWeb) { - showSnackBar( - context: context, - message: - 'removeFromSearch() not available in Flutter Web'); - return; - } - bool success = - await FlutterBranchSdk.removeFromSearch( - buo: buo!); - success = await FlutterBranchSdk.removeFromSearch( - buo: buo!, linkProperties: lp); - if (success) { - showSnackBar( - context: context, - message: 'Removed from Search'); - } - }, - ), - ), - ], - ), - CustomButton( - child: Text('Generate Link'), - onPressed: generateLink, - ), - StreamBuilder( - stream: controllerUrl.stream, - initialData: '', - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data!.isNotEmpty) { - return Column( - children: [ - Center( - child: Text( - 'Link build', - style: TextStyle( - color: Colors.blue, - fontWeight: FontWeight.bold), - )), - Center(child: Text(snapshot.data!)) - ], - ); - } else { - return Container(); - } - }, - ), - CustomButton( - child: Text('Share Link'), - onPressed: shareLink, - ), - SizedBox( - height: 10, - ), - Divider(), - Center( - child: Text( - 'Deep Link data', - style: TextStyle( - color: Colors.blue, fontWeight: FontWeight.bold), - ), - ), - Divider(), - StreamBuilder( - stream: controllerData.stream, - initialData: '', - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data!.isNotEmpty) { - return Column( - children: [ - Center(child: Text(snapshot.data!)), - ], - ); - } else { - return Container(); - } - }, - ), - ], - ), - ), - ), - ), - ); - } - - void generateLink() async { - BranchResponse response = - await FlutterBranchSdk.getShortUrl(buo: buo!, linkProperties: lp); - if (response.success) { - controllerUrl.sink.add('${response.result}'); - } else { - controllerUrl.sink - .add('Error : ${response.errorCode} - ${response.errorMessage}'); - } - } - - void shareLink() async { - BranchResponse response = await FlutterBranchSdk.showShareSheet( - buo: buo!, - linkProperties: lp, - messageText: 'My Share text', - androidMessageTitle: 'My Message Title', - androidSharingTitle: 'My Share with'); - - if (response.success) { - showSnackBar( - context: context, message: 'showShareSheet Success', duration: 5); - } else { - showSnackBar( - context: context, - message: - 'showShareSheet Error: ${response.errorCode} - ${response.errorMessage}', - duration: 5); - } - } - - void getLastAttributed() async { - BranchResponse response = - await FlutterBranchSdk.getLastAttributedTouchData(); - if (response.success) { - showSnackBar( - context: context, message: response.result.toString(), duration: 5); - } else { - showSnackBar( - context: context, - message: - 'showShareSheet Error: ${response.errorCode} - ${response.errorMessage}', - duration: 5); - } - } - - @override - void dispose() { - super.dispose(); - controllerData.close(); - controllerUrl.close(); - controllerInitSession.close(); - streamSubscription?.cancel(); - } -} +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_branch_sdk/flutter_branch_sdk.dart'; + +import 'custom_button.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: "Flutter Branch SDK Example", + debugShowCheckedModeBanner: false, + home: HomePage(), + ); + } +} + +class HomePage extends StatefulWidget { + const HomePage({Key? key}) : super(key: key); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + final GlobalKey scaffoldMessengerKey = + GlobalKey(); + + BranchContentMetaData metadata = BranchContentMetaData(); + BranchUniversalObject? buo; + BranchLinkProperties lp = BranchLinkProperties(); + BranchEvent? eventStandart; + BranchEvent? eventCustom; + + StreamSubscription? streamSubscription; + StreamController controllerData = StreamController(); + StreamController controllerInitSession = StreamController(); + + static const imageURL = + 'https://raw.githubusercontent.com/RodrigoSMarques/flutter_branch_sdk/master/assets/branch_logo_qrcode.jpeg'; + @override + void initState() { + super.initState(); + + listenDynamicLinks(); + + initDeepLinkData(); + + FlutterBranchSdk.setIdentity('branch_user_test'); + + //requestATTTracking(); + } + + void requestATTTracking() async { + AppTrackingStatus status; + status = await FlutterBranchSdk.requestTrackingAuthorization(); + if (kDebugMode) { + print(status); + } + + status = await FlutterBranchSdk.getTrackingAuthorizationStatus(); + if (kDebugMode) { + print(status); + } + + final uuid = await FlutterBranchSdk.getAdvertisingIdentifier(); + if (kDebugMode) { + print(uuid); + } + } + + void listenDynamicLinks() async { + streamSubscription = FlutterBranchSdk.initSession().listen((data) { + print('listenDynamicLinks - DeepLink Data: $data'); + controllerData.sink.add((data.toString())); + if (data.containsKey('+clicked_branch_link') && + data['+clicked_branch_link'] == true) { + print( + '------------------------------------Link clicked----------------------------------------------'); + print('Custom string: ${data['custom_string']}'); + print('Custom number: ${data['custom_number']}'); + print('Custom bool: ${data['custom_bool']}'); + print('Custom list number: ${data['custom_list_number']}'); + print( + '------------------------------------------------------------------------------------------------'); + showSnackBar( + message: 'Link clicked: Custom string - ${data['custom_string']}', + duration: 10); + } + }, onError: (error) { + print('InitSesseion error: ${error.toString()}'); + }); + } + + void initDeepLinkData() { + metadata = BranchContentMetaData() + ..addCustomMetadata('custom_string', 'abc') + ..addCustomMetadata('custom_number', 12345) + ..addCustomMetadata('custom_bool', true) + ..addCustomMetadata('custom_list_number', [1, 2, 3, 4, 5]) + ..addCustomMetadata('custom_list_string', ['a', 'b', 'c']) + //--optional Custom Metadata + ..contentSchema = BranchContentSchema.COMMERCE_PRODUCT + ..price = 50.99 + ..currencyType = BranchCurrencyType.BRL + ..quantity = 50 + ..sku = 'sku' + ..productName = 'productName' + ..productBrand = 'productBrand' + ..productCategory = BranchProductCategory.ELECTRONICS + ..productVariant = 'productVariant' + ..condition = BranchCondition.NEW + ..rating = 100 + ..ratingAverage = 50 + ..ratingMax = 100 + ..ratingCount = 2 + ..setAddress( + street: 'street', + city: 'city', + region: 'ES', + country: 'Brazil', + postalCode: '99999-987') + ..setLocation(31.4521685, -114.7352207); + + buo = BranchUniversalObject( + canonicalIdentifier: 'flutter/branch', + //parameter canonicalUrl + //If your content lives both on the web and in the app, make sure you set its canonical URL + // (i.e. the URL of this piece of content on the web) when building any BUO. + // By doing so, we’ll attribute clicks on the links that you generate back to their original web page, + // even if the user goes to the app instead of your website! This will help your SEO efforts. + canonicalUrl: 'https://flutter.dev', + title: 'Flutter Branch Plugin', + imageUrl: imageURL, + contentDescription: 'Flutter Branch Description', + /* + contentMetadata: BranchContentMetaData() + ..addCustomMetadata('custom_string', 'abc') + ..addCustomMetadata('custom_number', 12345) + ..addCustomMetadata('custom_bool', true) + ..addCustomMetadata('custom_list_number', [1, 2, 3, 4, 5]) + ..addCustomMetadata('custom_list_string', ['a', 'b', 'c']), + */ + //contentMetadata: metadata, + keywords: ['Plugin', 'Branch', 'Flutter'], + publiclyIndex: true, + locallyIndex: true, + expirationDateInMilliSec: DateTime.now() + .add(const Duration(days: 365)) + .millisecondsSinceEpoch); + + lp = BranchLinkProperties( + channel: 'facebook', + feature: 'sharing', + //parameter alias + //Instead of our standard encoded short url, you can specify the vanity alias. + // For example, instead of a random string of characters/integers, you can set the vanity alias as *.app.link/devonaustin. + // Aliases are enforced to be unique** and immutable per domain, and per link - they cannot be reused unless deleted. + //alias: 'https://branch.io' //define link url, + stage: 'new share', + campaign: 'campaign', + tags: ['one', 'two', 'three']) + ..addControlParam('\$uri_redirect_mode', '1') + ..addControlParam('referring_user_id', 'user_id'); + + eventStandart = BranchEvent.standardEvent(BranchStandardEvent.ADD_TO_CART) + //--optional Event data + ..transactionID = '12344555' + ..currency = BranchCurrencyType.BRL + ..revenue = 1.5 + ..shipping = 10.2 + ..tax = 12.3 + ..coupon = 'test_coupon' + ..affiliation = 'test_affiliation' + ..eventDescription = 'Event_description' + ..searchQuery = 'item 123' + ..adType = BranchEventAdType.BANNER + ..addCustomData( + 'Custom_Event_Property_Key1', 'Custom_Event_Property_val1') + ..addCustomData( + 'Custom_Event_Property_Key2', 'Custom_Event_Property_val2'); + + eventCustom = BranchEvent.customEvent('Custom_event') + ..addCustomData( + 'Custom_Event_Property_Key1', 'Custom_Event_Property_val1') + ..addCustomData( + 'Custom_Event_Property_Key2', 'Custom_Event_Property_val2'); + } + + void showSnackBar({required String message, int duration = 1}) { + scaffoldMessengerKey.currentState!.removeCurrentSnackBar(); + scaffoldMessengerKey.currentState!.showSnackBar( + SnackBar( + content: Text(message), + duration: Duration(seconds: duration), + ), + ); + } + + void validSdkIntegration() { + if (kIsWeb) { + showSnackBar( + message: 'validateSDKIntegration() not available in Flutter Web'); + return; + } + + FlutterBranchSdk.validateSDKIntegration(); + if (Platform.isAndroid) { + showSnackBar(message: 'Check messages in run log or logcat'); + } + } + + void enableTracking() { + FlutterBranchSdk.disableTracking(false); + showSnackBar(message: 'Tracking enabled'); + } + + void disableTracking() { + FlutterBranchSdk.disableTracking(true); + showSnackBar(message: 'Tracking disabled'); + } + + void identifyUser() { + FlutterBranchSdk.setIdentity('branch_user_test'); + showSnackBar(message: 'User branch_user_test identfied'); + } + + void userLogout() { + FlutterBranchSdk.logout(); + showSnackBar(message: 'User branch_user_test logout'); + } + + void registerView() { + FlutterBranchSdk.registerView(buo: buo!); + showSnackBar(message: 'Event Registered'); + } + + void trackContent() { + FlutterBranchSdk.trackContent(buo: [buo!], branchEvent: eventStandart!); + + FlutterBranchSdk.trackContent(buo: [buo!], branchEvent: eventCustom!); + + FlutterBranchSdk.trackContentWithoutBuo(branchEvent: eventStandart!); + + FlutterBranchSdk.trackContentWithoutBuo(branchEvent: eventCustom!); + + showSnackBar(message: 'Tracked content'); + } + + void getFirstParameters() async { + Map params = + await FlutterBranchSdk.getFirstReferringParams(); + controllerData.sink.add(params.toString()); + showSnackBar(message: 'First Parameters recovered'); + } + + void getLastParameters() async { + Map params = + await FlutterBranchSdk.getLatestReferringParams(); + controllerData.sink.add(params.toString()); + showSnackBar(message: 'Last Parameters recovered'); + } + + void getLastAttributed() async { + BranchResponse response = + await FlutterBranchSdk.getLastAttributedTouchData(); + if (response.success) { + controllerData.sink.add(response.result.toString()); + showSnackBar(message: 'Last Attributed TouchData recovered'); + } else { + showSnackBar( + message: + 'showShareSheet Error: ${response.errorCode} - ${response.errorMessage}', + duration: 5); + } + } + + void listOnSearch() async { + if (kIsWeb) { + showSnackBar(message: 'listOnSearch() not available in Flutter Web'); + return; + } + //Buo without Link Properties + bool success = await FlutterBranchSdk.listOnSearch(buo: buo!); + + //Buo with Link Properties + success = + await FlutterBranchSdk.listOnSearch(buo: buo!, linkProperties: lp); + + if (success) { + showSnackBar(message: 'Listed on Search'); + } + } + + void removeFromSearch() async { + if (kIsWeb) { + showSnackBar(message: 'removeFromSearch() not available in Flutter Web'); + return; + } + bool success = await FlutterBranchSdk.removeFromSearch(buo: buo!); + success = + await FlutterBranchSdk.removeFromSearch(buo: buo!, linkProperties: lp); + if (success) { + showSnackBar(message: 'Removed from Search'); + } + } + + void generateLink(BuildContext context) async { + BranchResponse response = + await FlutterBranchSdk.getShortUrl(buo: buo!, linkProperties: lp); + if (response.success) { + showGeneratedLink(this.context, response.result); + } else { + showSnackBar( + message: 'Error : ${response.errorCode} - ${response.errorMessage}'); + } + } + + void generateQrCode( + BuildContext context, + ) async { + /* + BranchResponse responseQrCodeData = await FlutterBranchSdk.getQRCodeAsData( + buo: buo!, + linkProperties: lp, + qrCode: BranchQrCode( + primaryColor: Colors.black, + //backgroundColor: const Color(0xff443a49), //Hex Color + centerLogoUrl: imageURL, + backgroundColor: Colors.white, + imageFormat: BranchImageFormat.PNG)); + if (responseQrCodeData.success) { + print(responseQrCodeData.result); + } else { + print( + 'Error : ${responseQrCodeData.errorCode} - ${responseQrCodeData.errorMessage}'); + } + + */ + BranchResponse responseQrCodeImage = + await FlutterBranchSdk.getQRCodeAsImage( + buo: buo!, + linkProperties: lp, + qrCode: BranchQrCode( + primaryColor: Colors.black, + //primaryColor: const Color(0xff443a49), //Hex colors + centerLogoUrl: imageURL, + backgroundColor: Colors.white, + imageFormat: BranchImageFormat.PNG)); + if (responseQrCodeImage.success) { + showQrCode(this.context, responseQrCodeImage.result); + } else { + showSnackBar( + message: + 'Error : ${responseQrCodeImage.errorCode} - ${responseQrCodeImage.errorMessage}'); + } + } + + void showGeneratedLink(BuildContext context, String url) async { + showModalBottomSheet( + isDismissible: true, + isScrollControlled: true, + context: context, + builder: (_) { + return Container( + padding: const EdgeInsets.all(12), + height: 150, + child: Column( + children: [ + const Center( + child: Text( + 'Link created', + style: TextStyle( + color: Colors.blue, fontWeight: FontWeight.bold), + )), + const SizedBox( + height: 10, + ), + Text(url), + const SizedBox( + height: 10, + ), + IntrinsicWidth( + stepWidth: 300, + child: CustomButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: url)); + Navigator.pop(this.context); + }, + child: const Center(child: Text('Copy link'))), + ), + ], + ), + ); + }); + } + + void showQrCode(BuildContext context, Image image) async { + showModalBottomSheet( + isDismissible: true, + isScrollControlled: true, + context: context, + builder: (_) { + return Container( + padding: const EdgeInsets.all(12), + height: 370, + child: Column( + children: [ + const Center( + child: Text( + 'Qr Code', + style: TextStyle( + color: Colors.blue, fontWeight: FontWeight.bold), + )), + const SizedBox( + height: 10, + ), + Image( + image: image.image, + height: 250, + width: 250, + ), + IntrinsicWidth( + stepWidth: 300, + child: CustomButton( + onPressed: () => Navigator.pop(this.context), + child: const Center(child: Text('Close'))), + ), + ], + ), + ); + }); + } + + void shareLink() async { + BranchResponse response = await FlutterBranchSdk.showShareSheet( + buo: buo!, + linkProperties: lp, + messageText: 'My Share text', + androidMessageTitle: 'My Message Title', + androidSharingTitle: 'My Share with'); + + if (response.success) { + showSnackBar(message: 'showShareSheet Success', duration: 5); + } else { + showSnackBar( + message: + 'showShareSheet Error: ${response.errorCode} - ${response.errorMessage}', + duration: 5); + } + } + + void shareWithLPLinkMetadata() async { + /// Create a BranchShareLink instance with a BranchUniversalObject and LinkProperties. + /// Set the BranchShareLink's LPLinkMetadata by using the addLPLinkMetadata() function. + ///Present the BranchShareLink's Share Sheet. + + ///Load icon from Assets + final iconData = (await rootBundle.load('assets/images/branch_logo.jpeg')) + .buffer + .asUint8List(); + + /* + ///Load icon from Web + final iconData = + (await NetworkAssetBundle(Uri.parse(imageURL)).load(imageURL)) + .buffer + .asUint8List(); + */ + + if (Platform.isIOS) { + FlutterBranchSdk.shareWithLPLinkMetadata( + buo: buo!, + linkProperties: lp, + title: "Share With LPLinkMetadata", + icon: iconData); + } else { + showSnackBar( + message: 'shareWithLPLinkMetadata() available only in iOS devices'); + return; + } + } + + @override + Widget build(BuildContext context) { + return ScaffoldMessenger( + key: scaffoldMessengerKey, + child: Scaffold( + appBar: AppBar( + title: const Text('Flutter Branch SDK Example'), + ), + body: Scrollbar( + thumbVisibility: true, + child: SingleChildScrollView( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + StreamBuilder( + stream: controllerInitSession.stream, + initialData: '', + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return Column( + children: [ + Center( + child: Text( + snapshot.data!, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.red), + )) + ], + ); + } else { + return Container(); + } + }, + ), + CustomButton( + onPressed: validSdkIntegration, + child: const Text('Validate SDK Integration'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: enableTracking, + child: const Text('Enable tracking'), + ), + ), + Expanded( + child: CustomButton( + onPressed: disableTracking, + child: const Text('Disable tracking'), + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: identifyUser, + child: const Text('Identify user'), + ), + ), + Expanded( + child: CustomButton( + onPressed: userLogout, + child: const Text('User logout'), + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: registerView, + child: const Text('Register view'), + ), + ), + Expanded( + child: CustomButton( + onPressed: trackContent, + child: const Text('Track content'), + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: getFirstParameters, + child: const Text('Get First Parameters', + textAlign: TextAlign.center), + ), + ), + Expanded( + child: CustomButton( + onPressed: getLastParameters, + child: const Text('Get Last Parameters', + textAlign: TextAlign.center), + ), + ), + Expanded( + child: CustomButton( + onPressed: getLastAttributed, + child: const Text('Get Last Attributed', + textAlign: TextAlign.center), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: listOnSearch, + child: const Text('List on Search', + textAlign: TextAlign.center), + ), + ), + Expanded( + child: CustomButton( + onPressed: removeFromSearch, + child: const Text('Remove from Search', + textAlign: TextAlign.center), + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CustomButton( + onPressed: () => generateLink(context), + child: const Text('Generate Link', + textAlign: TextAlign.center), + ), + ), + Expanded( + child: CustomButton( + onPressed: () => generateQrCode(context), + child: const Text('Generate QrCode', + textAlign: TextAlign.center), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: (CustomButton( + onPressed: shareLink, + child: + const Text('Share Link', textAlign: TextAlign.center), + ))), + Expanded( + child: CustomButton( + onPressed: shareWithLPLinkMetadata, + child: const Text('Share Link with LPLinkMetadata', + textAlign: TextAlign.center), + )) + ], + ), + const Divider(), + const Center( + child: Text( + 'Data', + style: TextStyle( + color: Colors.blue, fontWeight: FontWeight.bold), + ), + ), + const Divider(), + StreamBuilder( + stream: controllerData.stream, + initialData: null, + builder: (context, snapshot) { + if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return Column( + children: [ + Center(child: Text(snapshot.data!)), + ], + ); + } else { + return Container(); + } + }, + ), + ], + ), + ), + ), + ), + ); + } + + @override + void dispose() { + super.dispose(); + controllerData.close(); + controllerInitSession.close(); + streamSubscription?.cancel(); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index c7baa79f..80008c16 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -49,7 +49,7 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.5" fake_async: dependency: transitive description: @@ -63,19 +63,19 @@ packages: source: sdk version: "0.0.0" flutter_branch_sdk: - dependency: "direct dev" + dependency: "direct main" description: path: ".." relative: true source: path - version: "5.1.0" + version: "6.0.0" flutter_lints: - dependency: transitive + dependency: "direct dev" description: name: flutter_lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -99,7 +99,7 @@ packages: name: lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "2.0.0" matcher: dependency: transitive description: @@ -134,7 +134,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -190,5 +190,5 @@ packages: source: hosted version: "2.1.2" sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.17.3 <3.0.0" + flutter: ">=2.5.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7a13e348..7ac50593 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,63 +1,86 @@ -name: flutter_branch_sdk_example -description: Demonstrates how to use the flutter_branch_sdk plugin. -publish_to: 'none' - -environment: - sdk: ">=2.12.0-0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - -dev_dependencies: - flutter_test: - sdk: flutter - - flutter_branch_sdk: - path: ../ - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages +name: flutter_branch_sdk_example +description: Demonstrates how to use the flutter_branch_sdk plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.17.3 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + flutter_branch_sdk: + # When depending on this package from a real application you should use: + # flutter_branch_sdk: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + assets: + - assets/images/branch_logo.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..eb9b4d76 Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..d69c5669 Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ diff --git a/example/web/index.html b/example/web/index.html index a3d6b244..f5b3622b 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -1,54 +1,68 @@ - - - - - - - - - - - - - - - - - - - - flutter_branch_sdk_example - - - - - - - - - + + + + + + + + + + + + + + + + + + + + Flutter Branch SDK Example + + + + + + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json index 7e4b4461..e845a725 100644 --- a/example/web/manifest.json +++ b/example/web/manifest.json @@ -1,23 +1,35 @@ -{ - "name": "flutter_branch_sdk_example", - "short_name": "flutter_branch_sdk_example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "Demonstrates how to use the flutter_branch_sdk plugin.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} +{ + "description": "Demonstrates how to use the flutter_branch_sdk plugin.", + "name": "Flutter Branch SDK Example", + "short_name": "Flutter_Branch_SDK", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/ios/.gitignore b/ios/.gitignore index aa479fd3..f6ebbf64 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -1,37 +1,38 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 - -!default.pbxuser -!default.mode1v3 -!default.mode2v3 -!default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/Generated.xcconfig +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ /Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Classes/FlutterBranchIoSdkHelper.swift b/ios/Classes/FlutterBranchIoSdkHelper.swift index e490279a..12854000 100644 --- a/ios/Classes/FlutterBranchIoSdkHelper.swift +++ b/ios/Classes/FlutterBranchIoSdkHelper.swift @@ -196,6 +196,7 @@ func convertToEvent(dict: [String: Any?]) -> BranchEvent? { } return event } + func convertToAdType(adType: String) -> BranchEventAdType { switch adType { case "BANNER": @@ -210,6 +211,35 @@ func convertToAdType(adType: String) -> BranchEventAdType { return BranchEventAdType.none } } + +func convertToQRCode(dict: [String: Any?]) -> BranchQRCode { + let qrCode : BranchQRCode = BranchQRCode() + + if let width = dict["width"] as? Int { + qrCode.width = NSNumber(value: width) + } + if let margin = dict["margin"] as? Int { + qrCode.margin = NSNumber(value: margin) + } + if let codeColor = dict["codeColor"] as? String { + qrCode.codeColor = UIColor.init(hexString: codeColor) + } + if let backgroundColor = dict["backgroundColor"] as? String { + qrCode.backgroundColor = UIColor.init(hexString: backgroundColor) + } + if let imageFormat = dict["imageFormat"] as? String { + if (imageFormat == "JPEG") { + qrCode.imageFormat = BranchQRCodeImageFormat.JPEG + } else { + qrCode.imageFormat = BranchQRCodeImageFormat.PNG + } + } + if let centerLogoUrl = dict["centerLogoUrl"] as? String { + qrCode.centerLogo = centerLogoUrl + } + return qrCode +} + //--------------------------------------------------------------------------------------------- // Extension // -------------------------------------------------------------------------------------------- @@ -227,8 +257,63 @@ extension Date { extension Bundle { static func infoPlistValue(forKey key: String) -> Any? { guard let value = Bundle.main.object(forInfoDictionaryKey: key) else { - return nil + return nil } return value } + public var icon: UIImage? { + if let icons = infoDictionary?["CFBundleIcons"] as? [String: Any], + let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any], + let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String], + let lastIcon = iconFiles.last { + return UIImage(named: lastIcon) + } + return nil + } +} + +extension UIColor { + convenience init(hexString: String, alpha: CGFloat = 1.0) { + let hexString: String = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + let scanner = Scanner(string: hexString) + if (hexString.hasPrefix("#")) { + scanner.scanLocation = 1 + } + var color: UInt32 = 0 + scanner.scanHexInt32(&color) + let mask = 0x000000FF + let r = Int(color >> 16) & mask + let g = Int(color >> 8) & mask + let b = Int(color) & mask + let red = CGFloat(r) / 255.0 + let green = CGFloat(g) / 255.0 + let blue = CGFloat(b) / 255.0 + self.init(red:red, green:green, blue:blue, alpha:alpha) + } + func toHexString() -> String { + var r:CGFloat = 0 + var g:CGFloat = 0 + var b:CGFloat = 0 + var a:CGFloat = 0 + getRed(&r, green: &g, blue: &b, alpha: &a) + let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 + return String(format:"#%06x", rgb) + } +} + +extension UIImage { + public static func loadFrom(url: URL, completion: @escaping (_ image: UIImage?) -> ()) { + DispatchQueue.global().async { + if let data = try? Data(contentsOf: url) { + DispatchQueue.main.async { + completion(UIImage(data: data)) + } + } else { + DispatchQueue.main.async { + completion(nil) + } + } + } + } + } diff --git a/ios/Classes/SwiftFlutterBranchSdkPlugin.swift b/ios/Classes/SwiftFlutterBranchSdkPlugin.swift index 2b9cb17d..6b272bd8 100644 --- a/ios/Classes/SwiftFlutterBranchSdkPlugin.swift +++ b/ios/Classes/SwiftFlutterBranchSdkPlugin.swift @@ -10,7 +10,7 @@ let MESSAGE_CHANNEL = "flutter_branch_sdk/message"; let EVENT_CHANNEL = "flutter_branch_sdk/event"; let ERROR_CODE = "FLUTTER_BRANCH_SDK_ERROR"; let PLUGIN_NAME = "Flutter"; -let PLUGIN_VERSION = "5.1.1" +let PLUGIN_VERSION = "6.0.0" public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { var eventSink: FlutterEventSink? @@ -191,15 +191,6 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream case "validateSDKIntegration": validateSDKIntegration() break - case "loadRewards": - loadRewards(call: call, result: result) - break - case "redeemRewards": - redeemRewards(call: call, result: result) - break - case "getCreditHistory": - getCreditHistory(call: call, result: result) - break case "isUserIdentified": isUserIdentified(result: result) break @@ -230,6 +221,11 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream case "getLastAttributedTouchData": getLastAttributedTouchData(call: call, result: result) break + case "getQRCode": + getQRCode(call: call, result: result) + break + case"shareWithLPLinkMetadata": + shareWithLPLinkMetadata(call: call, result: result) default: result(FlutterMethodNotImplemented) break @@ -283,6 +279,9 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream if let err = (error as NSError?) { response["errorCode"] = String(err.code) response["errorMessage"] = err.localizedDescription + } else { + response["errorCode"] = "-1" + response["errorMessage"] = "Canceled by user" } } DispatchQueue.main.async { @@ -433,114 +432,6 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream } } - private func loadRewards(call: FlutterMethodCall, result: @escaping FlutterResult) { - let args = call.arguments as! [String: Any?] - let response : NSMutableDictionary! = [:] - - Branch.getInstance().loadRewards { (changed, error) in - if (error == nil) { - var credits : Int = 0 - if let bucket = args["bucket"] as? String { - credits = Branch.getInstance().getCreditsForBucket(bucket) - } else { - credits = Branch.getInstance().getCredits() - } - response["success"] = NSNumber(value: true) - response["credits"] = credits - } else { - let err = (error! as NSError) - response["success"] = NSNumber(value: false) - response["errorCode"] = String(err.code) - response["errorMessage"] = err.localizedDescription - } - DispatchQueue.main.async { - result(response) - } - } - } - - private func redeemRewards(call: FlutterMethodCall, result: @escaping FlutterResult) { - let args = call.arguments as! [String: Any?] - let count = args["count"] as! Int - let response : NSMutableDictionary! = [:] - - if let bucket = args["bucket"] as? String { - Branch.getInstance().redeemRewards(count, forBucket: bucket, callback: {(success, error) in - if success { - response["success"] = NSNumber(value: true) - } - else { - print("Failed to redeem credits: \(String(describing: error))") - let err = (error! as NSError) - response["success"] = NSNumber(value: false) - response["errorCode"] = String(err.code) - response["errorMessage"] = err.localizedDescription - } - DispatchQueue.main.async { - result(response) - } - }) - } else { - Branch.getInstance().redeemRewards(count, callback: {(success, error) in - if success { - response["success"] = NSNumber(value: true) - } - else { - print("Failed to redeem credits: \(String(describing: error))") - let err = (error! as NSError) - response["success"] = NSNumber(value: false) - response["errorCode"] = String(err.code) - response["errorMessage"] = err.localizedDescription - } - DispatchQueue.main.async { - result(response) - } - }) - } - } - - private func getCreditHistory(call: FlutterMethodCall, result: @escaping FlutterResult) { - let args = call.arguments as! [String: Any?] - let response : NSMutableDictionary! = [:] - let data : NSMutableDictionary! = [:] - - if let bucket = args["bucket"] as? String { - Branch.getInstance().getCreditHistory(forBucket: bucket, andCallback: { (creditHistory, error) in - if error == nil { - data["history"] = creditHistory - response["success"] = NSNumber(value: true) - response["data"] = data - } else { - print("Failed to redeem credits: \(String(describing: error))") - let err = (error! as NSError) - response["success"] = NSNumber(value: false) - response["errorCode"] = String(err.code) - response["errorMessage"] = err.localizedDescription - } - DispatchQueue.main.async { - result(response) - } - }) - } else { - Branch.getInstance().getCreditHistory { (creditHistory, error) in - if error == nil { - data["history"] = creditHistory - response["success"] = NSNumber(value: true) - response["data"] = data - } else { - print("Failed to redeem credits: \(String(describing: error))") - let err = (error! as NSError) - response["success"] = NSNumber(value: false) - response["errorCode"] = String(err.code) - response["errorMessage"] = err.localizedDescription - } - DispatchQueue.main.async { - result(response) - } - } - } - } - private func getLastAttributedTouchData(call: FlutterMethodCall, result: @escaping FlutterResult) { let args = call.arguments as! [String: Any?] @@ -584,6 +475,88 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream } } + private func setTimeout(call: FlutterMethodCall) { + let args = call.arguments as! [String: Any?] + let _ = args["timeout"] as? Int ?? 0 + } + + private func setConnectTimeout(call: FlutterMethodCall) { + let args = call.arguments as! [String: Any?] + let connectTimeout = args["connectTimeout"] as? Int ?? 0 + DispatchQueue.main.async { + Branch.getInstance().setNetworkTimeout(TimeInterval(connectTimeout)) + } + } + + private func setRetryCount(call: FlutterMethodCall) { + let args = call.arguments as! [String: Any?] + let _ = args["retryCount"] as? Int ?? 0 + } + + private func setRetryInterval(call: FlutterMethodCall) { + let args = call.arguments as! [String: Any?] + let retryInterval = args["retryInterval"] as? Int ?? 0 + DispatchQueue.main.async { + Branch.getInstance().setRetryInterval(TimeInterval(retryInterval)) + } + } + + private func getQRCode(call: FlutterMethodCall, result: @escaping FlutterResult) { + let args = call.arguments as! [String: Any?] + let buoDict = args["buo"] as! [String: Any?] + let lpDict = args["lp"] as! [String: Any?] + let qrCodeDict = args["qrCodeSettings"] as! [String: Any?] + + let buo: BranchUniversalObject? = convertToBUO(dict: buoDict) + let lp : BranchLinkProperties? = convertToLp(dict: lpDict ) + let qrCode : BranchQRCode? = convertToQRCode(dict: qrCodeDict) + + let response : NSMutableDictionary! = [:] + + qrCode?.getAsData(buo, linkProperties: lp, completion: { data, error in + if (error == nil) { + response["success"] = NSNumber(value: true) + response["result"] = FlutterStandardTypedData(bytes: data!) + } else { + response["success"] = NSNumber(value: false) + if let err = (error as NSError?) { + response["errorCode"] = String(err.code) + response["errorMessage"] = err.localizedDescription + } + } + DispatchQueue.main.async { + result(response) + } + + }) + } + + private func shareWithLPLinkMetadata(call: FlutterMethodCall, result: @escaping FlutterResult) { + + let args = call.arguments as! [String: Any?] + let buoDict = args["buo"] as! [String: Any?] + let lpDict = args["lp"] as! [String: Any?] + let messageText = args["messageText"] as! String + let buo: BranchUniversalObject? = convertToBUO(dict: buoDict) + let lp : BranchLinkProperties? = convertToLp(dict: lpDict ) + var iconImage : UIImage? + + if let iconData = args["iconData"] as? FlutterStandardTypedData { + iconImage = UIImage(data: iconData.data) + } else { + iconImage = Bundle.main.icon + } + + let bsl = BranchShareLink(universalObject: buo!, linkProperties: lp!) + if #available(iOS 13.0, *) { + bsl.addLPLinkMetadata(messageText, icon: iconImage) + let controller = UIApplication.shared.keyWindow!.rootViewController + bsl.presentActivityViewController(from: controller, anchor: nil) + } else { + showShareSheet(call: call, result: result) + } + } + /* https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager @@ -636,30 +609,4 @@ public class SwiftFlutterBranchSdkPlugin: NSObject, FlutterPlugin, FlutterStream } } } - - private func setTimeout(call: FlutterMethodCall) { - let args = call.arguments as! [String: Any?] - let _ = args["timeout"] as? Int ?? 0 - } - - private func setConnectTimeout(call: FlutterMethodCall) { - let args = call.arguments as! [String: Any?] - let connectTimeout = args["connectTimeout"] as? Int ?? 0 - DispatchQueue.main.async { - Branch.getInstance().setNetworkTimeout(TimeInterval(connectTimeout)) - } - } - - private func setRetryCount(call: FlutterMethodCall) { - let args = call.arguments as! [String: Any?] - let _ = args["retryCount"] as? Int ?? 0 - } - - private func setRetryInterval(call: FlutterMethodCall) { - let args = call.arguments as! [String: Any?] - let retryInterval = args["retryInterval"] as? Int ?? 0 - DispatchQueue.main.async { - Branch.getInstance().setRetryInterval(TimeInterval(retryInterval)) - } - } } diff --git a/ios/flutter_branch_sdk.podspec b/ios/flutter_branch_sdk.podspec index 2cb40fce..cbb7b684 100644 --- a/ios/flutter_branch_sdk.podspec +++ b/ios/flutter_branch_sdk.podspec @@ -1,24 +1,24 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint flutter_branch_sdk.podspec' to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'flutter_branch_sdk' - s.version = '3.0.0' - s.summary = 'Flutter Plugin for Brach Metrics SDK - https://branch.io' - s.description = <<-DESC -Flutter Plugin for Brach Metrics SDK - https://branch.io - DESC - s.homepage = 'https://github.com/RodrigoSMarques/flutter_branch_sdk' - s.license = { :file => '../LICENSE' } - s.author = { 'Rodrigo S. Marques' => 'rodrigosmarques@gmail.com' } - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.dependency 'Branch', '~> 1.42.0' - s.platform = :ios, '9.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint flutter_branch_sdk.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'flutter_branch_sdk' + s.version = '3.0.0' + s.summary = 'Flutter Plugin for create deep link using Brach SDK (https://branch.io). This plugin provides a cross-platform (iOS, Android, Web).' + s.description = <<-DESC +Flutter Plugin for create deep link using Brach SDK (https://branch.io). This plugin provides a cross-platform (iOS, Android, Web). + DESC + s.homepage = 'https://github.com/RodrigoSMarques/flutter_branch_sdk' + s.license = { :file => '../LICENSE' } + s.author = { 'Rodrigo S. Marques' => 'rodrigosmarques@gmail.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.dependency 'Branch', '~> 1.43.0' + s.platform = :ios, '9.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/lib/flutter_branch_sdk.dart b/lib/flutter_branch_sdk.dart index 640a6003..d8282e4e 100644 --- a/lib/flutter_branch_sdk.dart +++ b/lib/flutter_branch_sdk.dart @@ -1,9 +1,12 @@ -library flutter_branch_sdk; - -import 'src/app_tracking_transparency.dart'; -import 'src/flutter_branch_sdk_platform_interface.dart'; - -export 'src/app_tracking_transparency.dart'; -export 'src/branch_universal_object.dart'; - -part 'src/flutter_branch_sdk_plugin.dart'; +library flutter_branch_sdk; + +import 'dart:typed_data'; + +import 'src/flutter_branch_sdk_platform_interface.dart'; +import 'src/objects/app_tracking_transparency.dart'; +import 'src/objects/branch_universal_object.dart'; + +export 'src/objects/app_tracking_transparency.dart'; +export 'src/objects/branch_universal_object.dart'; + +part 'src/flutter_branch_sdk.dart'; diff --git a/lib/src/branch_event.dart b/lib/src/branch_event.dart deleted file mode 100644 index 3086a3ff..00000000 --- a/lib/src/branch_event.dart +++ /dev/null @@ -1,121 +0,0 @@ -part of flutter_branch_sdk_objects; -/* -* Enum for creating Branch events for tracking and analytical purpose. -* Enum class represent a standard or custom BranchEvents. Standard Branch events are defined with BRANCH_STANDARD_EVENT}. -* Please use #logEvent() method to log the events for tracking. -*/ - -enum BranchStandardEvent { - // Commerce events - ADD_TO_CART, - ADD_TO_WISHLIST, - VIEW_CART, - INITIATE_PURCHASE, - ADD_PAYMENT_INFO, - PURCHASE, - SPEND_CREDITS, - // Content Events - SEARCH, - VIEW_ITEM, - VIEW_ITEMS, - RATE, - SHARE, - // User Lifecycle Events - COMPLETE_REGISTRATION, - COMPLETE_TUTORIAL, - ACHIEVE_LEVEL, - UNLOCK_ACHIEVEMENT -} - -String getBranchStandardEventString(BranchStandardEvent branchStandardEvent) { - return branchStandardEvent.toString().split('.').last; -} - -enum BranchEventAdType { BANNER, INTERSTITIAL, REWARDED_VIDEO, NATIVE } - -String getBranchEventAdTypeString(BranchEventAdType branchEventAdType) { - return branchEventAdType.toString().split('.').last; -} - -class BranchEvent { - String _eventName = ''; - bool _isStandardEvent = true; - String transactionID = ''; - BranchCurrencyType? currency; - double revenue = -1; - double shipping = -1; - double tax = -1; - String coupon = ''; - String affiliation = ''; - String eventDescription = ''; - String searchQuery = ''; - BranchEventAdType? adType; - Map _customData = {}; - - BranchEvent.standardEvent(BranchStandardEvent branchStandardEvent) { - this._eventName = getBranchStandardEventString(branchStandardEvent); - this._isStandardEvent = true; - } - - BranchEvent.customEvent(this._eventName) { - this._isStandardEvent = false; - } - - String get eventName => _eventName; - bool get isStandardEvent => _isStandardEvent; - - void addCustomData(String key, dynamic value) { - this._customData[key] = value; - } - - void removeCustomData(String key) { - this._customData.remove(key); - } - - Map toMap() { - Map ret = Map(); - - ret["eventName"] = this._eventName; - ret["isStandardEvent"] = this._isStandardEvent; - if (this.transactionID.isNotEmpty) - ret["transactionID"] = this.transactionID; - if (this.currency != null) - ret["currency"] = getCurrencyTypeString(this.currency!); - if (this.revenue != -1) ret["revenue"] = this.revenue; - if (this.shipping != -1) ret["shipping"] = this.shipping; - if (this.tax != -1) ret["tax"] = this.tax; - if (this.coupon.isNotEmpty) ret["coupon"] = this.coupon; - if (this.affiliation.isNotEmpty) ret["affiliation"] = this.affiliation; - if (this.eventDescription.isNotEmpty) - ret["eventDescription"] = this.eventDescription; - if (this.searchQuery.isNotEmpty) ret["searchQuery"] = this.searchQuery; - if (this.adType != null) - ret["adType"] = getBranchEventAdTypeString(this.adType!); - if (this._customData.isNotEmpty) ret["customData"] = _customData; - return ret; - } - - Map toMapWeb() { - Map ret = Map(); - if (this._isStandardEvent) { - if (this.transactionID.isNotEmpty) - ret["transactionID"] = this.transactionID; - if (this.currency != null) - ret["currency"] = getCurrencyTypeString(this.currency!); - if (this.revenue != -1) ret["revenue"] = this.revenue; - if (this.shipping != -1) ret["shipping"] = this.shipping; - if (this.tax != -1) ret["tax"] = this.tax; - if (this.coupon.isNotEmpty) ret["coupon"] = this.coupon; - if (this.affiliation.isNotEmpty) ret["affiliation"] = this.affiliation; - if (this.eventDescription.isNotEmpty) - ret["eventDescription"] = this.eventDescription; - if (this.searchQuery.isNotEmpty) ret["searchQuery"] = this.searchQuery; - if (this.adType != null) - ret["adType"] = getBranchEventAdTypeString(this.adType!); - } - this._customData.forEach((key, value) { - ret['$key'] = value; - }); - return ret; - } -} diff --git a/lib/src/flutter_branch_sdk_plugin.dart b/lib/src/flutter_branch_sdk.dart similarity index 62% rename from lib/src/flutter_branch_sdk_plugin.dart rename to lib/src/flutter_branch_sdk.dart index 30fc7a56..b7bd045c 100644 --- a/lib/src/flutter_branch_sdk_plugin.dart +++ b/lib/src/flutter_branch_sdk.dart @@ -1,203 +1,206 @@ -part of flutter_branch_sdk; - -class FlutterBranchSdk { - static FlutterBranchSdkPlatform? __platform; - - static FlutterBranchSdkPlatform get _platform { - __platform ??= FlutterBranchSdkPlatform.instance; - return __platform!; - } - - @Deprecated('version 5.0.0') - static void initWeb({required String branchKey}) { - _platform.initWeb(branchKey: branchKey); - } - - ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value - static void setIdentity(String userId) { - _platform.setIdentity(userId); - } - - ///Add key value pairs to all requests - static void setRequestMetadata(String key, String value) { - _platform.setRequestMetadata(key, value); - } - - ///This method should be called if you know that a different person is about to use the app - static void logout() { - _platform.logout(); - } - - ///Returns the last parameters associated with the link that referred the user - static Future> getLatestReferringParams() async { - return await _platform.getLatestReferringParams(); - } - - ///Returns the first parameters associated with the link that referred the user - static Future> getFirstReferringParams() async { - return await _platform.getFirstReferringParams(); - } - - ///Method to change the Tracking state. If disabled SDK will not track any user data or state. - ///SDK will not send any network calls except for deep linking when tracking is disabled - static void disableTracking(bool value) async { - return _platform.disableTracking(value); - } - - ///Initialises a session with the Branch API - ///Listen click em Branch Deeplinks - static Stream> initSession() { - return _platform.initSession(); - } - - ///Use the SDK integration validator to check that you've added the Branch SDK and - ///handle deep links correctly when you first integrate Branch into your app. - static void validateSDKIntegration() { - _platform.validateSDKIntegration(); - } - - ///Creates a short url for the BUO - static Future getShortUrl( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties}) async { - return _platform.getShortUrl(buo: buo, linkProperties: linkProperties); - } - - ///Showing a Share Sheet - static Future showShareSheet( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties, - required String messageText, - String androidMessageTitle = '', - String androidSharingTitle = ''}) async { - return _platform.showShareSheet( - buo: buo, - linkProperties: linkProperties, - messageText: messageText, - androidMessageTitle: androidMessageTitle, - androidSharingTitle: androidSharingTitle); - } - - ///Logs this BranchEvent to Branch for tracking and analytics - static void trackContent( - {required List buo, - required BranchEvent branchEvent}) { - return _platform.trackContent(buo: buo, branchEvent: branchEvent); - } - - ///Logs this BranchEvent to Branch for tracking and analytics - static void trackContentWithoutBuo({required BranchEvent branchEvent}) { - return _platform.trackContentWithoutBuo(branchEvent: branchEvent); - } - - ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. - static void registerView({required BranchUniversalObject buo}) { - return _platform.registerView(buo: buo); - } - - ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search - ///For iOS: List items on Spotlight - static Future listOnSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - return _platform.listOnSearch(buo: buo, linkProperties: linkProperties); - } - - ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already - /// This will remove the content from Google(Firebase) and other supported Indexing services - ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed - static Future removeFromSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - return _platform.removeFromSearch(buo: buo, linkProperties: linkProperties); - } - - ///Retrieves rewards for the current user/session - @Deprecated('version 4.0.0') - static Future loadRewards({String bucket = 'default'}) async { - return _platform.loadRewards(bucket: bucket); - } - - ///Redeems the specified number of credits. if there are sufficient credits within it. - ///If the number to redeem exceeds the number available in the bucket, all of the - ///available credits will be redeemed instead. - @Deprecated('version 4.0.0') - static Future redeemRewards( - {required int count, String bucket = 'default'}) async { - return _platform.redeemRewards(count: count, bucket: bucket); - } - - ///Gets the credit history - @Deprecated('version 4.0.0') - static Future getCreditHistory( - {String bucket = 'default'}) async { - return _platform.getCreditHistory(bucket: bucket); - } - - ///Set time window for SKAdNetwork callouts in Hours (Only iOS) - ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. - static void setIOSSKAdNetworkMaxTime(int hours) { - return _platform.setIOSSKAdNetworkMaxTime(hours); - } - - ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. - ///If you call setIdentity, this device will have that identity associated with this user until logout is called. - ///This includes persisting through uninstalls, as we track device id. - static Future isUserIdentified() async { - return _platform.isUserIdentified(); - } - - /// request AppTracking Autorization and return AppTrackingStatus - /// on Android returns notSupported - static Future requestTrackingAuthorization() async { - return _platform.requestTrackingAuthorization(); - } - - /// return AppTrackingStatus - /// on Android returns notSupported - static Future getTrackingAuthorizationStatus() async { - return _platform.getTrackingAuthorizationStatus(); - } - - /// return advertising identifier (ie tracking data). - /// on Android returns empty string - static Future getAdvertisingIdentifier() async { - return _platform.getAdvertisingIdentifier(); - } - - ///Sets the duration in milliseconds that the system should wait for initializing - ///a network * request. - static void setConnectTimeout(int connectTimeout) { - return _platform.setConnectTimeout(connectTimeout); - } - - ///Sets the duration in milliseconds that the system should wait for a response - ///before timing out any Branch API. - ///Default 5500 ms. Note that this is the total time allocated for all request - ///retries as set in setRetryCount(int). - static void setTimeout(int timeout) { - return _platform.setTimeout(timeout); - } - - ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before - /// considering the request to have failed entirely. Default to 3. - /// Note that the the network timeout, as set in setNetworkTimeout(int), - /// together with the retry interval value from setRetryInterval(int) will - /// determine if the max retry count will be attempted. - static void setRetryCount(int retryCount) { - return _platform.setRetryCount(retryCount); - } - - ///Sets the amount of time in milliseconds to wait before re-attempting a - ///timed-out request to the Branch API. Default 1000 ms. - static void setRetryInterval(int retryInterval) { - return _platform.setRetryInterval(retryInterval); - } - - ///Gets the available last attributed touch data with a custom set attribution window. - static Future getLastAttributedTouchData( - {int? attributionWindow}) async { - return _platform.getLastAttributedTouchData( - attributionWindow: attributionWindow); - } -} +part of flutter_branch_sdk; + +class FlutterBranchSdk { + ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value + static void setIdentity(String userId) { + FlutterBranchSdkPlatform.instance.setIdentity(userId); + } + + ///Add key value pairs to all requests + static void setRequestMetadata(String key, String value) { + FlutterBranchSdkPlatform.instance.setRequestMetadata(key, value); + } + + ///This method should be called if you know that a different person is about to use the app + static void logout() { + FlutterBranchSdkPlatform.instance.logout(); + } + + ///Returns the last parameters associated with the link that referred the user + static Future> getLatestReferringParams() async { + return await FlutterBranchSdkPlatform.instance.getLatestReferringParams(); + } + + ///Returns the first parameters associated with the link that referred the user + static Future> getFirstReferringParams() async { + return await FlutterBranchSdkPlatform.instance.getFirstReferringParams(); + } + + ///Method to change the Tracking state. If disabled SDK will not track any user data or state. + ///SDK will not send any network calls except for deep linking when tracking is disabled + static void disableTracking(bool value) async { + return FlutterBranchSdkPlatform.instance.disableTracking(value); + } + + ///Initialises a session with the Branch API + ///Listen click em Branch Deeplinks + static Stream> initSession() { + return FlutterBranchSdkPlatform.instance.initSession(); + } + + ///Use the SDK integration validator to check that you've added the Branch SDK and + ///handle deep links correctly when you first integrate Branch into your app. + static void validateSDKIntegration() { + FlutterBranchSdkPlatform.instance.validateSDKIntegration(); + } + + ///Creates a short url for the BUO + static Future getShortUrl( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties}) async { + return FlutterBranchSdkPlatform.instance + .getShortUrl(buo: buo, linkProperties: linkProperties); + } + + ///Showing a Share Sheet + static Future showShareSheet( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required String messageText, + String androidMessageTitle = '', + String androidSharingTitle = ''}) async { + return FlutterBranchSdkPlatform.instance.showShareSheet( + buo: buo, + linkProperties: linkProperties, + messageText: messageText, + androidMessageTitle: androidMessageTitle, + androidSharingTitle: androidSharingTitle); + } + + ///Logs this BranchEvent to Branch for tracking and analytics + static void trackContent( + {required List buo, + required BranchEvent branchEvent}) { + return FlutterBranchSdkPlatform.instance + .trackContent(buo: buo, branchEvent: branchEvent); + } + + ///Logs this BranchEvent to Branch for tracking and analytics + static void trackContentWithoutBuo({required BranchEvent branchEvent}) { + return FlutterBranchSdkPlatform.instance + .trackContentWithoutBuo(branchEvent: branchEvent); + } + + ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. + static void registerView({required BranchUniversalObject buo}) { + return FlutterBranchSdkPlatform.instance.registerView(buo: buo); + } + + ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search + ///For iOS: List items on Spotlight + static Future listOnSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + return FlutterBranchSdkPlatform.instance + .listOnSearch(buo: buo, linkProperties: linkProperties); + } + + ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already + /// This will remove the content from Google(Firebase) and other supported Indexing services + ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed + static Future removeFromSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + return FlutterBranchSdkPlatform.instance + .removeFromSearch(buo: buo, linkProperties: linkProperties); + } + + ///Set time window for SKAdNetwork callouts in Hours (Only iOS) + ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. + static void setIOSSKAdNetworkMaxTime(int hours) { + return FlutterBranchSdkPlatform.instance.setIOSSKAdNetworkMaxTime(hours); + } + + ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. + ///If you call setIdentity, this device will have that identity associated with this user until logout is called. + ///This includes persisting through uninstalls, as we track device id. + static Future isUserIdentified() async { + return FlutterBranchSdkPlatform.instance.isUserIdentified(); + } + + /// request AppTracking Autorization and return AppTrackingStatus + /// on Android returns notSupported + static Future requestTrackingAuthorization() async { + return FlutterBranchSdkPlatform.instance.requestTrackingAuthorization(); + } + + /// return AppTrackingStatus + /// on Android returns notSupported + static Future getTrackingAuthorizationStatus() async { + return FlutterBranchSdkPlatform.instance.getTrackingAuthorizationStatus(); + } + + /// return advertising identifier (ie tracking data). + /// on Android returns empty string + static Future getAdvertisingIdentifier() async { + return FlutterBranchSdkPlatform.instance.getAdvertisingIdentifier(); + } + + ///Sets the duration in milliseconds that the system should wait for initializing + ///a network * request. + static void setConnectTimeout(int connectTimeout) { + return FlutterBranchSdkPlatform.instance.setConnectTimeout(connectTimeout); + } + + ///Sets the duration in milliseconds that the system should wait for a response + ///before timing out any Branch API. + ///Default 5500 ms. Note that this is the total time allocated for all request + ///retries as set in setRetryCount(int). + static void setTimeout(int timeout) { + return FlutterBranchSdkPlatform.instance.setTimeout(timeout); + } + + ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before + /// considering the request to have failed entirely. Default to 3. + /// Note that the the network timeout, as set in setNetworkTimeout(int), + /// together with the retry interval value from setRetryInterval(int) will + /// determine if the max retry count will be attempted. + static void setRetryCount(int retryCount) { + return FlutterBranchSdkPlatform.instance.setRetryCount(retryCount); + } + + ///Sets the amount of time in milliseconds to wait before re-attempting a + ///timed-out request to the Branch API. Default 1000 ms. + static void setRetryInterval(int retryInterval) { + return FlutterBranchSdkPlatform.instance.setRetryInterval(retryInterval); + } + + ///Gets the available last attributed touch data with a custom set attribution window. + static Future getLastAttributedTouchData( + {int? attributionWindow}) async { + return FlutterBranchSdkPlatform.instance + .getLastAttributedTouchData(attributionWindow: attributionWindow); + } + + ///Creates a Branch QR Code image. Returns the QR code as Uint8List. + static Future getQRCodeAsData( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCode}) async { + return FlutterBranchSdkPlatform.instance.getQRCodeAsData( + buo: buo, linkProperties: linkProperties, qrCodeSettings: qrCode); + } + + ///Creates a Branch QR Code image. Returns the QR code as a Image. + static Future getQRCodeAsImage( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCode}) async { + return FlutterBranchSdkPlatform.instance.getQRCodeAsImage( + buo: buo, linkProperties: linkProperties, qrCodeSettings: qrCode); + } + + static void shareWithLPLinkMetadata( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required Uint8List icon, + required String title}) { + Map params = {}; + params['buo'] = buo.toMap(); + params['lp'] = linkProperties.toMap(); + params['title'] = title; + + FlutterBranchSdkPlatform.instance.shareWithLPLinkMetadata( + buo: buo, linkProperties: linkProperties, icon: icon, title: title); + } +} diff --git a/lib/src/flutter_branch_sdk_mobile.dart b/lib/src/flutter_branch_sdk_method_channel.dart similarity index 55% rename from lib/src/flutter_branch_sdk_mobile.dart rename to lib/src/flutter_branch_sdk_method_channel.dart index 37d7a201..2c943f23 100644 --- a/lib/src/flutter_branch_sdk_mobile.dart +++ b/lib/src/flutter_branch_sdk_method_channel.dart @@ -1,384 +1,340 @@ -import 'dart:io'; - -import 'package:flutter/services.dart'; - -import 'app_tracking_transparency.dart'; -import 'flutter_branch_sdk_platform_interface.dart'; - -class FlutterBranchSdkMobile implements FlutterBranchSdkPlatform { - static const _MESSAGE_CHANNEL = 'flutter_branch_sdk/message'; - static const _EVENT_CHANNEL = 'flutter_branch_sdk/event'; - - static const MethodChannel _messageChannel = - const MethodChannel(_MESSAGE_CHANNEL); - - static const EventChannel _eventChannel = const EventChannel(_EVENT_CHANNEL); - - static Stream? _initSessionStream; - - static FlutterBranchSdkMobile? _singleton; - - /// Constructs a singleton instance of [FlutterBranchSdkMobile]. - factory FlutterBranchSdkMobile() { - if (_singleton == null) { - _singleton = FlutterBranchSdkMobile._(); - } - return _singleton!; - } - - FlutterBranchSdkMobile._(); - - ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value - - @Deprecated('version 5.0.0') - @override - void initWeb({required String branchKey}) { - //nothing - } - - ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value - @override - void setIdentity(String userId) { - Map _params = {}; - _params['userId'] = userId; - _messageChannel.invokeMethod('setIdentity', _params); - } - - ///Add key value pairs to all requests - @override - void setRequestMetadata(String key, String value) { - Map _params = {}; - _params['key'] = key; - _params['value'] = value; - - _messageChannel.invokeMethod('setRequestMetadata', _params); - } - - ///This method should be called if you know that a different person is about to use the app - @override - void logout() { - _messageChannel.invokeMethod('logout'); - } - - ///Returns the last parameters associated with the link that referred the user - @override - Future> getLatestReferringParams() async { - return await _messageChannel.invokeMethod('getLatestReferringParams'); - } - - ///Returns the first parameters associated with the link that referred the user - @override - Future> getFirstReferringParams() async { - return await _messageChannel.invokeMethod('getFirstReferringParams'); - } - - ///Method to change the Tracking state. If disabled SDK will not track any user data or state. - ///SDK will not send any network calls except for deep linking when tracking is disabled - @override - void disableTracking(bool value) async { - Map _params = {}; - _params['disable'] = value; - _messageChannel.invokeMethod('setTrackingDisabled', _params); - } - - ///Initialises a session with the Branch API - ///Listen click em Branch Deeplinks - @override - Stream> initSession() { - if (_initSessionStream == null) - _initSessionStream = - _eventChannel.receiveBroadcastStream().cast>(); - - return _initSessionStream!; - } - - ///Use the SDK integration validator to check that you've added the Branch SDK and - ///handle deep links correctly when you first integrate Branch into your app. - @override - void validateSDKIntegration() { - _messageChannel.invokeMethod('validateSDKIntegration'); - } - - ///Creates a short url for the BUO - @override - Future getShortUrl( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties}) async { - Map _params = {}; - _params['buo'] = buo.toMap(); - _params['lp'] = linkProperties.toMap(); - - Map response = - await _messageChannel.invokeMethod('getShortUrl', _params); - - if (response['success']) { - return BranchResponse.success(result: response['url']); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } - - ///Showing a Share Sheet - @override - Future showShareSheet( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties, - required String messageText, - String androidMessageTitle = '', - String androidSharingTitle = ''}) async { - Map _params = {}; - _params['buo'] = buo.toMap(); - _params['lp'] = linkProperties.toMap(); - _params['messageText'] = messageText; - _params['messageTitle'] = androidMessageTitle; - _params['sharingTitle'] = androidSharingTitle; - - Map response = - await _messageChannel.invokeMethod('showShareSheet', _params); - - if (response['success']) { - return BranchResponse.success(result: response['url']); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } - - ///Logs this BranchEvent to Branch for tracking and analytics - @override - void trackContent( - {required List buo, - required BranchEvent branchEvent}) { - Map _params = {}; - - _params['buo'] = buo.map((b) => b.toMap()).toList(); - if (branchEvent.toMap().isNotEmpty) { - _params['event'] = branchEvent.toMap(); - } - _messageChannel.invokeMethod('trackContent', _params); - } - - ///Logs this BranchEvent to Branch for tracking and analytics - @override - void trackContentWithoutBuo({required BranchEvent branchEvent}) { - Map _params = {}; - - if (branchEvent.toMap().isEmpty) { - throw ArgumentError('branchEvent is required'); - } - _params['event'] = branchEvent.toMap(); - - _messageChannel.invokeMethod('trackContentWithoutBuo', _params); - } - - ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. - @override - void registerView({required BranchUniversalObject buo}) { - Map _params = {}; - - _params['buo'] = buo.toMap(); - - _messageChannel.invokeMethod('registerView', _params); - } - - ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search - ///For iOS: List items on Spotlight - @override - Future listOnSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - Map _params = {}; - - _params['buo'] = buo.toMap(); - if (linkProperties != null && linkProperties.toMap().isNotEmpty) { - _params['lp'] = linkProperties.toMap(); - } - - return await _messageChannel.invokeMethod('listOnSearch', _params); - } - - ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already - /// This will remove the content from Google(Firebase) and other supported Indexing services - ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed - @override - Future removeFromSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - Map _params = {}; - _params['buo'] = buo.toMap(); - if (linkProperties != null && linkProperties.toMap().isNotEmpty) { - _params['lp'] = linkProperties.toMap(); - } - return await _messageChannel.invokeMethod('removeFromSearch', _params); - } - - ///Retrieves rewards for the current user/session - @override - Future loadRewards({String bucket = 'default'}) async { - Map _params = {}; - _params['bucket'] = bucket; - - Map response = - await _messageChannel.invokeMethod('loadRewards', _params); - - if (response['success']) { - return BranchResponse.success(result: response['credits']); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } - - ///Redeems the specified number of credits. if there are sufficient credits within it. - ///If the number to redeem exceeds the number available in the bucket, all of the - ///available credits will be redeemed instead. - @override - Future redeemRewards( - {required int count, String bucket = 'default'}) async { - Map _params = {}; - _params['count'] = count; - _params['bucket'] = bucket; - - Map response = - await _messageChannel.invokeMethod('redeemRewards', _params); - - if (response['success']) { - return BranchResponse.success(result: true); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } - - ///Gets the credit history - @override - Future getCreditHistory({String bucket = 'default'}) async { - Map _params = {}; - _params['bucket'] = bucket; - - Map response = - await _messageChannel.invokeMethod('getCreditHistory', _params); - - print('GetCreditHistory ${response.toString()}'); - - if (response['success']) { - return BranchResponse.success(result: response['data']['history']); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } - - ///Set time window for SKAdNetwork callouts in Hours (Only iOS) - ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. - @override - void setIOSSKAdNetworkMaxTime(int hours) { - if (!Platform.isIOS) { - return; - } - - Map _params = {}; - _params['maxTimeInterval'] = hours; - _messageChannel.invokeMethod('setSKAdNetworkMaxTime', _params); - } - - ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. - ///If you call setIdentity, this device will have that identity associated with this user until logout is called. - ///This includes persisting through uninstalls, as we track device id. - @override - Future isUserIdentified() async { - return await _messageChannel.invokeMethod('isUserIdentified'); - } - - /// request AppTracking Autorization and return AppTrackingStatus - /// on Android returns notSupported - @override - Future requestTrackingAuthorization() async { - if (!Platform.isIOS) { - return AppTrackingStatus.notSupported; - } - final int status = (await _messageChannel - .invokeMethod('requestTrackingAuthorization'))!; - return AppTrackingStatus.values[status]; - } - - /// return AppTrackingStatus - /// on Android returns notSupported - @override - Future getTrackingAuthorizationStatus() async { - if (!Platform.isIOS) { - return AppTrackingStatus.notSupported; - } - final int status = (await _messageChannel - .invokeMethod('getTrackingAuthorizationStatus'))!; - return AppTrackingStatus.values[status]; - } - - /// return advertising identifier (ie tracking data). - /// on Android returns empty string - @override - Future getAdvertisingIdentifier() async { - if (!Platform.isIOS) { - return ""; - } - - final String uuid = (await _messageChannel - .invokeMethod('getAdvertisingIdentifier'))!; - return uuid; - } - - @override - void setConnectTimeout(int connectTimeout) { - Map _params = {}; - _params['connectTimeout'] = connectTimeout; - _messageChannel.invokeMethod('setConnectTimeout', _params); - } - - @override - void setRetryCount(int retryCount) { - Map _params = {}; - _params['retryCount'] = retryCount; - _messageChannel.invokeMethod('setRetryCount', _params); - } - - @override - void setRetryInterval(int retryInterval) { - Map _params = {}; - _params['retryInterval'] = retryInterval; - _messageChannel.invokeMethod('setRetryInterval', _params); - } - - @override - void setTimeout(int timeout) { - Map _params = {}; - _params['timeout'] = timeout; - _messageChannel.invokeMethod('setTimeout', _params); - } - - @override - Future getLastAttributedTouchData( - {int? attributionWindow}) async { - Map params = {}; - - if (attributionWindow != null) { - params['attributionWindow'] = attributionWindow; - } - - Map response = await _messageChannel.invokeMethod( - 'getLastAttributedTouchData', params); - - if (response['success']) { - return BranchResponse.success(result: response['data']['latd']); - } else { - return BranchResponse.error( - errorCode: response['errorCode'], - errorMessage: response['errorMessage']); - } - } -} +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import 'flutter_branch_sdk_platform_interface.dart'; +import 'objects/app_tracking_transparency.dart'; +import 'objects/branch_universal_object.dart'; + +/// An implementation of [FlutterBranchSdkPlatform] that uses method channels. +class FlutterBranchSdkMethodChannel implements FlutterBranchSdkPlatform { + static const MESSAGE_CHANNEL = 'flutter_branch_sdk/message'; + static const EVENT_CHANNEL = 'flutter_branch_sdk/event'; + + /// The method channel used to interact with the native platform. + final messageChannel = const MethodChannel(MESSAGE_CHANNEL); + final eventChannel = const EventChannel(EVENT_CHANNEL); + + static Stream>? _initSessionStream; + + ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value + @override + void setIdentity(String userId) { + messageChannel.invokeMethod('setIdentity', {'userId': userId}); + } + + ///Add key value pairs to all requests + @override + void setRequestMetadata(String key, String value) { + messageChannel + .invokeMethod('setRequestMetadata', {'key': key, 'value': value}); + } + + ///This method should be called if you know that a different person is about to use the app + @override + void logout() { + messageChannel.invokeMethod('logout'); + } + + ///Returns the last parameters associated with the link that referred the user + @override + Future> getLatestReferringParams() async { + return await messageChannel.invokeMethod('getLatestReferringParams'); + } + + ///Returns the first parameters associated with the link that referred the user + @override + Future> getFirstReferringParams() async { + return await messageChannel.invokeMethod('getFirstReferringParams'); + } + + ///Method to change the Tracking state. If disabled SDK will not track any user data or state. + ///SDK will not send any network calls except for deep linking when tracking is disabled + @override + void disableTracking(bool value) async { + messageChannel.invokeMethod('setTrackingDisabled', {'disable': value}); + } + + ///Initialises a session with the Branch API + ///Listen click em Branch Deeplinks + @override + Stream> initSession() { + _initSessionStream ??= + eventChannel.receiveBroadcastStream().cast>(); + + return _initSessionStream!; + } + + ///Use the SDK integration validator to check that you've added the Branch SDK and + ///handle deep links correctly when you first integrate Branch into your app. + @override + void validateSDKIntegration() { + messageChannel.invokeMethod('validateSDKIntegration'); + } + + ///Creates a short url for the BUO + @override + Future getShortUrl( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties}) async { + Map response = await messageChannel.invokeMethod( + 'getShortUrl', {'buo': buo.toMap(), 'lp': linkProperties.toMap()}); + + if (response['success']) { + return BranchResponse.success(result: response['url']); + } else { + return BranchResponse.error( + errorCode: response['errorCode'], + errorMessage: response['errorMessage']); + } + } + + ///Showing a Share Sheet + @override + Future showShareSheet( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required String messageText, + String androidMessageTitle = '', + String androidSharingTitle = ''}) async { + Map response = + await messageChannel.invokeMethod('showShareSheet', { + 'buo': buo.toMap(), + 'lp': linkProperties.toMap(), + 'messageText': messageText, + 'messageTitle': androidMessageTitle, + 'sharingTitle': androidSharingTitle + }); + + if (response['success']) { + return BranchResponse.success(result: response['url']); + } else { + return BranchResponse.error( + errorCode: response['errorCode'], + errorMessage: response['errorMessage']); + } + } + + ///Logs this BranchEvent to Branch for tracking and analytics + @override + void trackContent( + {required List buo, + required BranchEvent branchEvent}) { + Map params = {}; + + params['buo'] = buo.map((b) => b.toMap()).toList(); + if (branchEvent.toMap().isNotEmpty) { + params['event'] = branchEvent.toMap(); + } + messageChannel.invokeMethod('trackContent', params); + } + + ///Logs this BranchEvent to Branch for tracking and analytics + @override + void trackContentWithoutBuo({required BranchEvent branchEvent}) { + if (branchEvent.toMap().isEmpty) { + throw ArgumentError('branchEvent is required'); + } + messageChannel + .invokeMethod('trackContentWithoutBuo', {'event': branchEvent.toMap()}); + } + + ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. + @override + void registerView({required BranchUniversalObject buo}) { + messageChannel.invokeMethod('registerView', {'buo': buo.toMap()}); + } + + ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search + ///For iOS: List items on Spotlight + @override + Future listOnSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + Map params = {}; + params['buo'] = buo.toMap(); + if (linkProperties != null && linkProperties.toMap().isNotEmpty) { + params['lp'] = linkProperties.toMap(); + } + return await messageChannel.invokeMethod('listOnSearch', params); + } + + ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already + /// This will remove the content from Google(Firebase) and other supported Indexing services + ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed + @override + Future removeFromSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + Map params = {}; + params['buo'] = buo.toMap(); + if (linkProperties != null && linkProperties.toMap().isNotEmpty) { + params['lp'] = linkProperties.toMap(); + } + return await messageChannel.invokeMethod('removeFromSearch', params); + } + + ///Set time window for SKAdNetwork callouts in Hours (Only iOS) + ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. + @override + void setIOSSKAdNetworkMaxTime(int hours) { + if (!Platform.isIOS) { + return; + } + messageChannel + .invokeMethod('setSKAdNetworkMaxTime', {'maxTimeInterval': hours}); + } + + ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. + ///If you call setIdentity, this device will have that identity associated with this user until logout is called. + ///This includes persisting through uninstalls, as we track device id. + @override + Future isUserIdentified() async { + return await messageChannel.invokeMethod('isUserIdentified'); + } + + /// request AppTracking Autorization and return AppTrackingStatus + /// on Android returns notSupported + @override + Future requestTrackingAuthorization() async { + if (!Platform.isIOS) { + return AppTrackingStatus.notSupported; + } + final int status = (await messageChannel + .invokeMethod('requestTrackingAuthorization'))!; + return AppTrackingStatus.values[status]; + } + + /// return AppTrackingStatus + /// on Android returns notSupported + @override + Future getTrackingAuthorizationStatus() async { + if (!Platform.isIOS) { + return AppTrackingStatus.notSupported; + } + final int status = (await messageChannel + .invokeMethod('getTrackingAuthorizationStatus'))!; + return AppTrackingStatus.values[status]; + } + + /// return advertising identifier (ie tracking data). + /// on Android returns empty string + @override + Future getAdvertisingIdentifier() async { + if (!Platform.isIOS) { + return ""; + } + final String uuid = (await messageChannel + .invokeMethod('getAdvertisingIdentifier'))!; + return uuid; + } + + @override + void setConnectTimeout(int connectTimeout) { + messageChannel + .invokeMethod('setConnectTimeout', {'connectTimeout': connectTimeout}); + } + + @override + void setRetryCount(int retryCount) { + messageChannel.invokeMethod('setRetryCount', {'retryCount': retryCount}); + } + + @override + void setRetryInterval(int retryInterval) { + messageChannel + .invokeMethod('setRetryInterval', {'retryInterval': retryInterval}); + } + + @override + void setTimeout(int timeout) { + messageChannel.invokeMethod('setTimeout', {'timeout': timeout}); + } + + @override + Future getLastAttributedTouchData( + {int? attributionWindow}) async { + Map params = {}; + + if (attributionWindow != null) { + params['attributionWindow'] = attributionWindow; + } + + Map response = + await messageChannel.invokeMethod('getLastAttributedTouchData', params); + + if (response['success']) { + return BranchResponse.success(result: response['data']['latd']); + } else { + return BranchResponse.error( + errorCode: response['errorCode'], + errorMessage: response['errorMessage']); + } + } + + ///Creates a Branch QR Code image. Returns the QR code as Uint8List. + @override + Future getQRCodeAsData( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + Map response = + await messageChannel.invokeMethod('getQRCode', { + 'buo': buo.toMap(), + 'lp': linkProperties.toMap(), + 'qrCodeSettings': qrCodeSettings.toMap() + }); + + if (response['success']) { + return BranchResponse.success(result: response['result']); + } else { + return BranchResponse.error( + errorCode: response['errorCode'], + errorMessage: response['errorMessage']); + } + } + + ///Creates a Branch QR Code image. Returns the QR code as a Image. + @override + Future getQRCodeAsImage( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + Map response = + await messageChannel.invokeMethod('getQRCode', { + 'buo': buo.toMap(), + 'lp': linkProperties.toMap(), + 'qrCodeSettings': qrCodeSettings.toMap() + }); + + if (response['success']) { + return BranchResponse.success(result: Image.memory(response['result'])); + } else { + return BranchResponse.error( + errorCode: response['errorCode'], + errorMessage: response['errorMessage']); + } + } + + ///Share with LPLinkMetadata on iOS + @override + void shareWithLPLinkMetadata( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required Uint8List icon, + required String title}) async { + Map params = {}; + params['buo'] = buo.toMap(); + params['lp'] = linkProperties.toMap(); + params['messageText'] = title; + params['iconData'] = icon; + + if (Platform.isIOS) { + messageChannel.invokeMethod('shareWithLPLinkMetadata', params); + } else { + messageChannel.invokeMethod('showShareSheet', {params}); + } + } +} diff --git a/lib/src/flutter_branch_sdk_platform_interface.dart b/lib/src/flutter_branch_sdk_platform_interface.dart index 647b1e9a..5c0c2bd4 100644 --- a/lib/src/flutter_branch_sdk_platform_interface.dart +++ b/lib/src/flutter_branch_sdk_platform_interface.dart @@ -1,222 +1,221 @@ -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'app_tracking_transparency.dart'; -import 'branch_universal_object.dart'; -import 'flutter_branch_sdk_mobile.dart'; - -export 'branch_universal_object.dart'; - -/// The interface that all implementations of flutter_branch_sdk must -/// implement. -abstract class FlutterBranchSdkPlatform extends PlatformInterface { - /// Constructs an instance of [FlutterBranchSdkPlatform]. - FlutterBranchSdkPlatform() : super(token: _token); - - static FlutterBranchSdkPlatform _instance = FlutterBranchSdkMobile(); - - static final Object _token = Object(); - - /// The default instance of [FlutterLocalNotificationsPlatform] to use. - static FlutterBranchSdkPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FlutterBranchSdkPlatform] when they register - /// themselves. - static set instance(FlutterBranchSdkPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - @Deprecated('version 5.0.0') - void initWeb({required String branchKey}) { - throw UnimplementedError('initWeb has not been implemented'); - } - - ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value - void setIdentity(String userId) { - throw UnimplementedError('setIdentity has not been implemented'); - } - - ///Add key value pairs to all requests - void setRequestMetadata(String key, String value) { - throw UnimplementedError('setRequestMetadata has not been implemented'); - } - - ///This method should be called if you know that a different person is about to use the app - void logout() { - throw UnimplementedError('logout has not been implemented'); - } - - ///Returns the last parameters associated with the link that referred the user - Future> getLatestReferringParams() async { - throw UnimplementedError( - 'getLatestReferringParams has not been implemented'); - } - - ///Returns the first parameters associated with the link that referred the user - Future> getFirstReferringParams() async { - throw UnimplementedError( - 'getFirstReferringParams has not been implemented'); - } - - ///Method to change the Tracking state. If disabled SDK will not track any user data or state. - ///SDK will not send any network calls except for deep linking when tracking is disabled - void disableTracking(bool value) async { - throw UnimplementedError('disableTracking has not been implemented'); - } - - ///Initialises a session with the Branch API - ///Listen click em Branch Deeplinks - Stream> initSession() { - throw UnimplementedError('initSession has not been implemented'); - } - - ///Use the SDK integration validator to check that you've added the Branch SDK and - ///handle deep links correctly when you first integrate Branch into your app. - void validateSDKIntegration() { - throw UnimplementedError('validateSDKIntegration has not been implemented'); - } - - ///Creates a short url for the BUO - Future getShortUrl( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties}) async { - throw UnimplementedError('getShortUrl has not been implemented'); - } - - ///Showing a Share Sheet - Future showShareSheet( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties, - required String messageText, - String androidMessageTitle = '', - String androidSharingTitle = ''}) async { - throw UnimplementedError('showShareSheet has not been implemented'); - } - - ///Logs this BranchEvent to Branch for tracking and analytics - void trackContent( - {required List buo, - required BranchEvent branchEvent}) { - throw UnimplementedError('trackContent has not been implemented'); - } - - ///Logs this BranchEvent to Branch for tracking and analytics - void trackContentWithoutBuo({required BranchEvent branchEvent}) { - throw UnimplementedError('trackContentWithoutBuo has not been implemented'); - } - - ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. - void registerView({required BranchUniversalObject buo}) { - throw UnimplementedError('registerView has not been implemented'); - } - - ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search - ///For iOS: List items on Spotlight - Future listOnSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - throw UnimplementedError('listOnSearch has not been implemented'); - } - - ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already - /// This will remove the content from Google(Firebase) and other supported Indexing services - ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed - Future removeFromSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - throw UnimplementedError('removeFromSearch has not been implemented'); - } - - ///Retrieves rewards for the current user/session - @Deprecated('version 4.0.0') - Future loadRewards({String bucket = 'default'}) async { - throw UnimplementedError('loadRewards has not been implemented'); - } - - ///Redeems the specified number of credits. if there are sufficient credits within it. - ///If the number to redeem exceeds the number available in the bucket, all of the - ///available credits will be redeemed instead. - @Deprecated('version 4.0.0') - Future redeemRewards( - {required int count, String bucket = 'default'}) async { - throw UnimplementedError('redeemRewards has not been implemented'); - } - - ///Gets the credit history - @Deprecated('version 4.0.0') - Future getCreditHistory({String bucket = 'default'}) async { - throw UnimplementedError('getCreditHistory has not been implemented'); - } - - ///Set time window for SKAdNetwork callouts in Hours (Only iOS) - ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. - void setIOSSKAdNetworkMaxTime(int hours) { - throw UnimplementedError( - 'setIOSSKAdNetworkMaxTime has not been implemented'); - } - - ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. - ///If you call setIdentity, this device will have that identity associated with this user until logout is called. - ///This includes persisting through uninstalls, as we track device id. - Future isUserIdentified() async { - throw UnimplementedError('isUserIdentified has not been implemented'); - } - - /// request AppTracking Autorization and return AppTrackingStatus - /// on Android returns notSupported - Future requestTrackingAuthorization() async { - throw UnimplementedError( - 'requestTrackingAuthorization has not been implemented'); - } - - /// return AppTrackingStatus - /// on Android returns notSupported - Future getTrackingAuthorizationStatus() async { - throw UnimplementedError( - 'getTrackingAuthorizationStatus has not been implemented'); - } - - /// return advertising identifier (ie tracking data). - /// on Android returns empty string - Future getAdvertisingIdentifier() async { - throw UnimplementedError( - 'getAdvertisingIdentifier has not been implemented'); - } - - ///Sets the duration in milliseconds that the system should wait for initializing - ///a network * request. - void setConnectTimeout(int connectTimeout) { - throw UnimplementedError('setConnectTimeout has not been implemented'); - } - - ///Sets the duration in milliseconds that the system should wait for a response - ///before timing out any Branch API. - ///Default 5500 ms. Note that this is the total time allocated for all request - ///retries as set in setRetryCount(int). - void setTimeout(int timeout) { - throw UnimplementedError('setTimeout has not been implemented'); - } - - ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before - /// considering the request to have failed entirely. Default to 3. - /// Note that the the network timeout, as set in setNetworkTimeout(int), - /// together with the retry interval value from setRetryInterval(int) will - /// determine if the max retry count will be attempted. - void setRetryCount(int retryCount) { - throw UnimplementedError('setRetryCount has not been implemented'); - } - - ///Sets the amount of time in milliseconds to wait before re-attempting a - ///timed-out request to the Branch API. Default 1000 ms. - void setRetryInterval(int retryInterval) { - throw UnimplementedError('setRetryInterval has not been implemented'); - } - - ///Gets the available last attributed touch data with a custom set attribution window. - Future getLastAttributedTouchData( - {int? attributionWindow}) async { - throw UnimplementedError( - 'getLastAttributedTouchData has not been implemented'); - } -} +import 'dart:typed_data'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'flutter_branch_sdk_method_channel.dart'; +import 'objects/app_tracking_transparency.dart'; +import 'objects/branch_universal_object.dart'; + +abstract class FlutterBranchSdkPlatform extends PlatformInterface { + /// Constructs a FlutterBranchSdkPlatform. + FlutterBranchSdkPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FlutterBranchSdkPlatform _instance = FlutterBranchSdkMethodChannel(); + + /// The default instance of [FlutterBranchSdkPlatform] to use. + /// + /// Defaults to [FlutterBranchSdkMethodChannel]. + static FlutterBranchSdkPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [FlutterBranchSdkPlatform] when + /// they register themselves. + static set instance(FlutterBranchSdkPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value + void setIdentity(String userId) { + throw UnimplementedError('setIdentity has not been implemented'); + } + + ///Add key value pairs to all requests + void setRequestMetadata(String key, String value) { + throw UnimplementedError('setRequestMetadata has not been implemented'); + } + + ///This method should be called if you know that a different person is about to use the app + void logout() { + throw UnimplementedError('logout has not been implemented'); + } + + ///Returns the last parameters associated with the link that referred the user + Future> getLatestReferringParams() async { + throw UnimplementedError( + 'getLatestReferringParams has not been implemented'); + } + + ///Returns the first parameters associated with the link that referred the user + Future> getFirstReferringParams() async { + throw UnimplementedError( + 'getFirstReferringParams has not been implemented'); + } + + ///Method to change the Tracking state. If disabled SDK will not track any user data or state. + ///SDK will not send any network calls except for deep linking when tracking is disabled + void disableTracking(bool value) async { + throw UnimplementedError('disableTracking has not been implemented'); + } + + ///Initialises a session with the Branch API + ///Listen click em Branch Deeplinks + Stream> initSession() { + throw UnimplementedError('initSession has not been implemented'); + } + + ///Use the SDK integration validator to check that you've added the Branch SDK and + ///handle deep links correctly when you first integrate Branch into your app. + void validateSDKIntegration() { + throw UnimplementedError('validateSDKIntegration has not been implemented'); + } + + ///Creates a short url for the BUO + Future getShortUrl( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties}) async { + throw UnimplementedError('getShortUrl has not been implemented'); + } + + ///Showing a Share Sheet + Future showShareSheet( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required String messageText, + String androidMessageTitle = '', + String androidSharingTitle = ''}) async { + throw UnimplementedError('showShareSheet has not been implemented'); + } + + ///Logs this BranchEvent to Branch for tracking and analytics + void trackContent( + {required List buo, + required BranchEvent branchEvent}) { + throw UnimplementedError('trackContent has not been implemented'); + } + + ///Logs this BranchEvent to Branch for tracking and analytics + void trackContentWithoutBuo({required BranchEvent branchEvent}) { + throw UnimplementedError('trackContentWithoutBuo has not been implemented'); + } + + ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. + void registerView({required BranchUniversalObject buo}) { + throw UnimplementedError('registerView has not been implemented'); + } + + ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search + ///For iOS: List items on Spotlight + Future listOnSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + throw UnimplementedError('listOnSearch has not been implemented'); + } + + ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already + /// This will remove the content from Google(Firebase) and other supported Indexing services + ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed + Future removeFromSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + throw UnimplementedError('removeFromSearch has not been implemented'); + } + + ///Set time window for SKAdNetwork callouts in Hours (Only iOS) + ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. + void setIOSSKAdNetworkMaxTime(int hours) { + throw UnimplementedError( + 'setIOSSKAdNetworkMaxTime has not been implemented'); + } + + ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. + ///If you call setIdentity, this device will have that identity associated with this user until logout is called. + ///This includes persisting through uninstalls, as we track device id. + Future isUserIdentified() async { + throw UnimplementedError('isUserIdentified has not been implemented'); + } + + /// request AppTracking Autorization and return AppTrackingStatus + /// on Android returns notSupported + Future requestTrackingAuthorization() async { + throw UnimplementedError( + 'requestTrackingAuthorization has not been implemented'); + } + + /// return AppTrackingStatus + /// on Android returns notSupported + Future getTrackingAuthorizationStatus() async { + throw UnimplementedError( + 'getTrackingAuthorizationStatus has not been implemented'); + } + + /// return advertising identifier (ie tracking data). + /// on Android returns empty string + Future getAdvertisingIdentifier() async { + throw UnimplementedError( + 'getAdvertisingIdentifier has not been implemented'); + } + + ///Sets the duration in milliseconds that the system should wait for initializing + ///a network * request. + void setConnectTimeout(int connectTimeout) { + throw UnimplementedError('setConnectTimeout has not been implemented'); + } + + ///Sets the duration in milliseconds that the system should wait for a response + ///before timing out any Branch API. + ///Default 5500 ms. Note that this is the total time allocated for all request + ///retries as set in setRetryCount(int). + void setTimeout(int timeout) { + throw UnimplementedError('setTimeout has not been implemented'); + } + + ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before + /// considering the request to have failed entirely. Default to 3. + /// Note that the the network timeout, as set in setNetworkTimeout(int), + /// together with the retry interval value from setRetryInterval(int) will + /// determine if the max retry count will be attempted. + void setRetryCount(int retryCount) { + throw UnimplementedError('setRetryCount has not been implemented'); + } + + ///Sets the amount of time in milliseconds to wait before re-attempting a + ///timed-out request to the Branch API. Default 1000 ms. + void setRetryInterval(int retryInterval) { + throw UnimplementedError('setRetryInterval has not been implemented'); + } + + ///Gets the available last attributed touch data with a custom set attribution window. + Future getLastAttributedTouchData( + {int? attributionWindow}) async { + throw UnimplementedError( + 'getLastAttributedTouchData has not been implemented'); + } + + ///Creates a Branch QR Code image. Returns the QR code as Uint8List. + Future getQRCodeAsData( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + throw UnimplementedError('getQRCodeAsData has not been implemented'); + } + + ///Creates a Branch QR Code image. Returns the QR code as a Image. + Future getQRCodeAsImage( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + throw UnimplementedError('getQRCodeAsImage has not been implemented'); + } + + void shareWithLPLinkMetadata( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required Uint8List icon, + required String title}) { + throw UnimplementedError( + 'shareWithLPLinkMetadata has not been implemented'); + } +} diff --git a/lib/src/flutter_branch_sdk_web.dart b/lib/src/flutter_branch_sdk_web.dart index 4924d43a..53a071f3 100644 --- a/lib/src/flutter_branch_sdk_web.dart +++ b/lib/src/flutter_branch_sdk_web.dart @@ -1,482 +1,457 @@ -import 'dart:async'; -import 'dart:convert'; -// ignore: avoid_web_libraries_in_flutter -import 'dart:js'; -// ignore: avoid_web_libraries_in_flutter -import 'dart:js_util'; - -import 'package:flutter_web_plugins/flutter_web_plugins.dart'; - -import 'app_tracking_transparency.dart'; -import 'flutter_branch_sdk_platform_interface.dart'; -import 'web/branch_js.dart'; - -/// A workaround to deep-converting an object from JS to a Dart Object. -dynamic _jsObjectToDartObject(data) => json.decode(jsonStringify(data)); - -dynamic _dartObjectToJsObject(data) => jsonParse(json.encode(data)); -Map _metaData = {}; - -/// A web implementation of the FlutterBranchSdk plugin. -class FlutterBranchSdk extends FlutterBranchSdkPlatform { - static FlutterBranchSdk? _singleton; - - /// Constructs a singleton instance of [MethodChannelFlutterBranchSdk]. - factory FlutterBranchSdk() { - if (_singleton == null) { - _singleton = FlutterBranchSdk._(); - } - return _singleton!; - } - - FlutterBranchSdk._(); - - /// Registers this class as the default instance of [SharePlatform]. - static void registerWith(Registrar registrar) { - FlutterBranchSdkPlatform.instance = FlutterBranchSdk(); - } - - static final StreamController> _initSessionStream = - StreamController>(); - static bool _userIdentified = false; - - @Deprecated('version 5.0.0') - @override - void initWeb({required String branchKey}) {} - - ///Initialises a session with the Branch API - ///Listen click em Branch Deeplinks - @override - Stream> initSession() { - getLatestReferringParams().then((data) { - if (data.isNotEmpty) { - _initSessionStream.sink - .add(data.map((key, value) => MapEntry('$key', value))); - } else { - _initSessionStream.sink.add({}); - } - }); - - return _initSessionStream.stream; - } - - ///Returns the last parameters associated with the link that referred the user, not really applicaple for web though - @override - Future> getLatestReferringParams() { - final Completer> response = Completer(); - - try { - BranchJS.data(allowInterop((err, data) { - if (err == null) { - if (data != null) { - var responseData = - Map.from(_jsObjectToDartObject(data)); - response.complete(responseData['data_parsed'] ?? {}); - } else { - response.complete({}); - } - } else { - response.completeError(err); - } - })); - } catch (e) { - print('getLatestReferringParams() error: $e'); - response.completeError(e); - } - return response.future; - } - - ///Returns the first parameters associated with the link that referred the user - @override - Future> getFirstReferringParams() { - final Completer> response = - Completer>(); - - try { - BranchJS.first(allowInterop((err, data) { - if (err == null) { - if (data != null) { - var responseData = - Map.from(_jsObjectToDartObject(data)); - response.complete(responseData['data_parsed'] ?? {}); - } else { - response.complete({}); - } - } else { - response.completeError(err); - } - })); - } catch (e) { - print('getFirstReferringParams() error: $e'); - response.completeError(e); - } - return response.future; - } - - ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value - @override - void setIdentity(String userId) { - try { - BranchJS.setIdentity(userId, allowInterop((error, data) { - if (error == null) { - _userIdentified = true; - } - })); - } catch (e) { - print('setIdentity() error: $e'); - } - } - - ///This method should be called if you know that a different person is about to use the app - @override - void logout() { - try { - BranchJS.logout(allowInterop((error) { - if (error == null) { - _userIdentified = false; - } - })); - } catch (e) { - print('logout() error: $e'); - } - } - - ///Method to change the Tracking state. If disabled SDK will not track any user data or state. - ///SDK will not send any network calls except for deep linking when tracking is disabled - @override - void disableTracking(bool value) { - try { - BranchJS.disableTracking(value); - } catch (e) { - print('disableTracking() error: $e'); - } - } - - ///Creates a short url for the BUO - @override - Future getShortUrl( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties}) async { - Map data = buo.toMapWeb(); - linkProperties.getControlParams().forEach((key, value) { - data['$key'] = value; - }); - - Map linkData = { - ...linkProperties.toMapWeb(), - 'data': data - }; - - Completer responseCompleter = Completer(); - - try { - BranchJS.link(_dartObjectToJsObject(linkData), allowInterop((err, url) { - if (err == null) { - responseCompleter.complete(BranchResponse.success(result: url)); - } else { - responseCompleter.completeError( - BranchResponse.error(errorCode: '-1', errorMessage: err)); - } - })); - } catch (e) { - print('getShortUrl() error: $e'); - responseCompleter.completeError(BranchResponse.error( - errorCode: '-1', errorMessage: 'getShortUrl() error')); - } - return responseCompleter.future; - } - - ///Showing a Share Sheet - Implemented via navigator share if available, otherwise browser prompt. - @override - Future showShareSheet( - {required BranchUniversalObject buo, - required BranchLinkProperties linkProperties, - required String messageText, - String androidMessageTitle = '', - String androidSharingTitle = ''}) async { - BranchResponse response = - await getShortUrl(buo: buo, linkProperties: linkProperties); - if (response.success) { - try { - await promiseToFuture(navigatorShare(_dartObjectToJsObject({ - "title": messageText, - "text": buo.title, - "url": response.result - }))); - } catch (e) { - browserPrompt(messageText, response.result); - } - } - return response; - } - - ///Logs this BranchEvent to Branch for tracking and analytics - @override - void trackContent( - {required List buo, - required BranchEvent branchEvent}) { - JsArray contentItems = JsArray(); - - buo.forEach((element) { - contentItems.add(_dartObjectToJsObject(element.toMapWeb())); - }); - - try { - BranchJS.logEvent(branchEvent.eventName, - _dartObjectToJsObject(branchEvent.toMapWeb()), contentItems); - } catch (e) { - print('trackContent() error: $e'); - } - } - - ///Logs this BranchEvent to Branch for tracking and analytics - @override - void trackContentWithoutBuo({required BranchEvent branchEvent}) { - try { - BranchJS.logEvent( - branchEvent.eventName, _dartObjectToJsObject(branchEvent.toMapWeb())); - } catch (e) { - print('trackContentWithoutBuo() error: $e'); - } - } - - ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. - @override - void registerView({required BranchUniversalObject buo}) { - BranchEvent branchEvent = - BranchEvent.standardEvent(BranchStandardEvent.VIEW_ITEM); - - // This might not be exactly the same thing as BUO.registerView, but there's no clear implementation for web sdk - trackContent(buo: [buo], branchEvent: branchEvent); - } - - ///Add key value pairs to all requests - @override - void setRequestMetadata(String key, String value) { - _metaData[key] = value; - } - - ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search - ///For iOS: List items on Spotlight - @override - Future listOnSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - throw UnsupportedError('listOnSearch() Not supported by Branch JS SDK'); - } - - ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already - /// This will remove the content from Google(Firebase) and other supported Indexing services - ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed - @override - Future removeFromSearch( - {required BranchUniversalObject buo, - BranchLinkProperties? linkProperties}) async { - throw UnsupportedError('removeFromSearch() Not supported by Branch JS SDK'); - } - - ///Retrieves rewards for the current user/session - @Deprecated('version 4.0.0') - @override - Future loadRewards({String bucket = 'default'}) async { - Completer responseCompleter = Completer(); - - try { - BranchJS.credits(allowInterop((err, data) { - if (err == null) { - var parsedData = Map.from(_jsObjectToDartObject(data)); - if (parsedData.isNotEmpty) { - responseCompleter.complete(BranchResponse.success( - result: parsedData.containsKey(bucket) - ? parsedData[bucket] - : parsedData['default'])); - } else { - responseCompleter.complete(BranchResponse.success(result: 0)); - } - } else { - responseCompleter.complete( - BranchResponse.error(errorCode: '999', errorMessage: err)); - } - })); - } catch (e) { - print('loadRewards() error: $e'); - responseCompleter.complete(BranchResponse.error( - errorCode: '-1', errorMessage: 'loadRewards() error')); - } - return responseCompleter.future; - } - - ///Redeems the specified number of credits. if there are sufficient credits within it. - ///If the number to redeem exceeds the number available in the bucket, all of the - ///available credits will be redeemed instead. - @Deprecated('version 4.0.0') - @override - Future redeemRewards( - {required int count, String bucket = 'default'}) async { - Completer responseCompleter = Completer(); - - try { - BranchJS.redeem(count, bucket, allowInterop((err) { - if (err == null) { - responseCompleter.complete(BranchResponse.success(result: true)); - } else { - responseCompleter.complete(BranchResponse.error( - errorCode: '999', errorMessage: err.toString())); - } - })); - } catch (e) { - print('redeemRewards() error: $e'); - responseCompleter.complete(BranchResponse.error( - errorCode: '-1', errorMessage: 'redeemRewards() error')); - } - return responseCompleter.future; - } - - ///Gets the credit history - @Deprecated('version 4.0.0') - @override - Future getCreditHistory({String bucket = 'default'}) async { - Completer responseCompleter = Completer(); - - try { - BranchJS.creditHistory(_dartObjectToJsObject({'bucket': bucket}), - allowInterop((err, data) { - if (err == null) { - if (data != null) { - responseCompleter.complete( - BranchResponse.success(result: _jsObjectToDartObject(data))); - } else { - responseCompleter.complete(BranchResponse.success(result: {})); - } - } else { - responseCompleter.complete(BranchResponse.error( - errorCode: '999', errorMessage: err.toString())); - } - })); - } catch (e) { - print('getCreditHistory() error: $e'); - responseCompleter.complete(BranchResponse.error( - errorCode: '-1', errorMessage: 'getCreditHistory() error')); - } - - return responseCompleter.future; - } - - ///Set time window for SKAdNetwork callouts in Hours (Only iOS) - ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. - @override - void setIOSSKAdNetworkMaxTime(int hours) { - throw UnsupportedError( - 'setIOSSKAdNetworkMaxTime() Not available in Branch JS SDK'); - } - - ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. - ///If you call setIdentity, this device will have that identity associated with this user until logout is called. - ///This includes persisting through uninstalls, as we track device id. - // NOTE: This is not really accurate for persistent checks... - @override - Future isUserIdentified() async { - return Future.value(_userIdentified); - } - - /// request AppTracking Autorization and return AppTrackingStatus - /// on Android returns notSupported - @override - Future requestTrackingAuthorization() async { - throw UnsupportedError( - 'requestTrackingAuthorization() Not available in Branch JS SDK'); - } - - /// return AppTrackingStatus - /// on Android returns notSupported - @override - Future getTrackingAuthorizationStatus() async { - throw UnsupportedError( - 'getTrackingAuthorizationStatus() Not available in Branch JS SDK'); - } - - /// return advertising identifier (ie tracking data). - /// on Android returns empty string - @override - Future getAdvertisingIdentifier() async { - throw UnsupportedError( - 'getAdvertisingIdentifier() Not available in Branch JS SDK'); - } - - ///Use the SDK integration validator to check that you've added the Branch SDK and - ///handle deep links correctly when you first integrate Branch into your app. - @override - void validateSDKIntegration() { - throw UnsupportedError( - 'validateSDKIntegration() not available in Branch JS SDK'); - } - - ///Sets the duration in milliseconds that the system should wait for initializing - ///a network * request. - @override - void setConnectTimeout(int connectTimeout) { - throw UnsupportedError( - 'setConnectTimeout() Not available in Branch JS SDK'); - } - - ///Sets the duration in milliseconds that the system should wait for a response - ///before timing out any Branch API. - ///Default 5500 ms. Note that this is the total time allocated for all request - ///retries as set in setRetryCount(int). - @override - void setTimeout(int timeout) { - throw UnsupportedError('setTimeout() Not available in Branch JS SDK'); - } - - ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before - /// considering the request to have failed entirely. Default to 3. - /// Note that the the network timeout, as set in setNetworkTimeout(int), - /// together with the retry interval value from setRetryInterval(int) will - /// determine if the max retry count will be attempted. - @override - void setRetryCount(int retryCount) { - throw UnsupportedError('setRetryCount() Not available in Branch JS SDK'); - } - - ///Sets the amount of time in milliseconds to wait before re-attempting a - ///timed-out request to the Branch API. Default 1000 ms. - @override - void setRetryInterval(int retryInterval) { - throw UnsupportedError('setRetryInterval() Not available in Branch JS SDK'); - } - - ///Gets the available last attributed touch data with a custom set attribution window. - @override - Future getLastAttributedTouchData( - {int? attributionWindow}) async { - Completer responseCompleter = Completer(); - - try { - BranchJS.lastAttributedTouchData(attributionWindow, - allowInterop((err, data) { - if (err == null) { - if (data != null) { - print(data); - responseCompleter.complete( - BranchResponse.success(result: _jsObjectToDartObject(data))); - } else { - responseCompleter.complete(BranchResponse.success(result: {})); - } - } else { - responseCompleter.complete(BranchResponse.error( - errorCode: '999', errorMessage: err.toString())); - } - })); - } catch (error) { - print('getLastAttributedTouchData() error: $error'); - responseCompleter.complete(BranchResponse.error( - errorCode: '-1', errorMessage: 'getLastAttributedTouchData() error')); - } - - return responseCompleter.future; - } - - void close() { - _initSessionStream.close(); - } -} +// In order to *not* need this ignore, consider extracting the "web" version +// of your plugin as a separate package, instead of inlining it in the same +// package as the core of your plugin. +// ignore: avoid_web_libraries_in_flutter +import 'dart:async'; +import 'dart:convert'; +import 'dart:js'; +import 'dart:js_util'; +import 'dart:typed_data'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +import 'flutter_branch_sdk_platform_interface.dart'; +import 'objects/app_tracking_transparency.dart'; +import 'objects/branch_universal_object.dart'; +import 'web/branch_js.dart'; + +/// A workaround to deep-converting an object from JS to a Dart Object. +dynamic _jsObjectToDartObject(data) => json.decode(jsonStringify(data)); + +dynamic _dartObjectToJsObject(data) => jsonParse(json.encode(data)); +Map _metaData = {}; + +/// A web implementation of the FlutterBranchSdkPlatform of the FlutterBranchSdk plugin. +class FlutterBranchSdkWeb extends FlutterBranchSdkPlatform { + /// Constructs a FlutterBranchSdkWeb + FlutterBranchSdkWeb(); + + static void registerWith(Registrar registrar) { + FlutterBranchSdkPlatform.instance = FlutterBranchSdkWeb(); + } + + static final StreamController> _initSessionStream = + StreamController>(); + static bool _userIdentified = false; + + ///Initialises a session with the Branch API + ///Listen click em Branch Deeplinks + @override + Stream> initSession() { + getLatestReferringParams().then((data) { + if (data.isNotEmpty) { + _initSessionStream.sink + .add(data.map((key, value) => MapEntry('$key', value))); + } else { + _initSessionStream.sink.add({}); + } + }); + + return _initSessionStream.stream; + } + + ///Returns the last parameters associated with the link that referred the user, not really applicaple for web though + @override + Future> getLatestReferringParams() { + final Completer> response = Completer(); + + try { + BranchJS.data(allowInterop((err, data) { + if (err == null) { + if (data != null) { + var responseData = + Map.from(_jsObjectToDartObject(data)); + response.complete(responseData['data_parsed'] ?? {}); + } else { + response.complete({}); + } + } else { + response.completeError(err); + } + })); + } catch (e) { + debugPrint('getLatestReferringParams() error: ${e.toString()}'); + response.completeError(e); + } + return response.future; + } + + ///Returns the first parameters associated with the link that referred the user + @override + Future> getFirstReferringParams() { + final Completer> response = + Completer>(); + + try { + BranchJS.first(allowInterop((err, data) { + if (err == null) { + if (data != null) { + var responseData = + Map.from(_jsObjectToDartObject(data)); + response.complete(responseData['data_parsed'] ?? {}); + } else { + response.complete({}); + } + } else { + response.completeError(err); + } + })); + } catch (e) { + debugPrint('getFirstReferringParams() error: ${e.toString()}'); + response.completeError(e); + } + return response.future; + } + + ///Identifies the current user to the Branch API by supplying a unique identifier as a userId value + @override + void setIdentity(String userId) { + try { + BranchJS.setIdentity(userId, allowInterop((error, data) { + if (error == null) { + _userIdentified = true; + } + })); + } catch (e) { + debugPrint('setIdentity() error: ${e.toString()}'); + } + } + + ///This method should be called if you know that a different person is about to use the app + @override + void logout() { + try { + BranchJS.logout(allowInterop((error) { + if (error == null) { + _userIdentified = false; + } + })); + } catch (e) { + debugPrint('logout() error: ${e.toString()}'); + } + } + + ///Method to change the Tracking state. If disabled SDK will not track any user data or state. + ///SDK will not send any network calls except for deep linking when tracking is disabled + @override + void disableTracking(bool value) { + try { + BranchJS.disableTracking(value); + } catch (e) { + debugPrint('disableTracking() error: ${e.toString()}'); + } + } + + ///Creates a short url for the BUO + @override + Future getShortUrl( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties}) async { + Map data = buo.toMap(); + linkProperties.getControlParams().forEach((key, value) { + data[key] = value; + }); + + Map linkData = {...linkProperties.toMap(), 'data': data}; + + Completer responseCompleter = Completer(); + + try { + BranchJS.link(_dartObjectToJsObject(linkData), allowInterop((err, url) { + if (err == null) { + responseCompleter.complete(BranchResponse.success(result: url)); + } else { + responseCompleter.completeError( + BranchResponse.error(errorCode: '-1', errorMessage: err)); + } + })); + } catch (e) { + debugPrint('getShortUrl() error: ${e.toString()}'); + responseCompleter.completeError(BranchResponse.error( + errorCode: '-1', errorMessage: 'getShortUrl() error')); + } + return responseCompleter.future; + } + + ///Showing a Share Sheet - Implemented via navigator share if available, otherwise browser prompt. + @override + Future showShareSheet( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required String messageText, + String androidMessageTitle = '', + String androidSharingTitle = ''}) async { + BranchResponse response = + await getShortUrl(buo: buo, linkProperties: linkProperties); + if (response.success) { + try { + await promiseToFuture(navigatorShare(_dartObjectToJsObject({ + "title": messageText, + "text": buo.title, + "url": response.result + }))); + } catch (e) { + browserPrompt(messageText, response.result); + } + } + return response; + } + + ///Logs this BranchEvent to Branch for tracking and analytics + @override + void trackContent( + {required List buo, + required BranchEvent branchEvent}) { + JsArray contentItems = JsArray(); + + for (var element in buo) { + contentItems.add(_dartObjectToJsObject(element.toMap())); + } + + try { + BranchJS.logEvent(branchEvent.eventName, + _dartObjectToJsObject(branchEvent.toMap()), contentItems); + } catch (e) { + debugPrint('trackContent() error: ${e.toString()}'); + } + } + + ///Logs this BranchEvent to Branch for tracking and analytics + @override + void trackContentWithoutBuo({required BranchEvent branchEvent}) { + try { + BranchJS.logEvent( + branchEvent.eventName, _dartObjectToJsObject(branchEvent.toMap())); + } catch (e) { + debugPrint('trackContentWithoutBuo() error: ${e.toString()}'); + } + } + + ///Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. + @override + void registerView({required BranchUniversalObject buo}) { + BranchEvent branchEvent = + BranchEvent.standardEvent(BranchStandardEvent.VIEW_ITEM); + + // This might not be exactly the same thing as BUO.registerView, but there's no clear implementation for web sdk + trackContent(buo: [buo], branchEvent: branchEvent); + } + + ///Add key value pairs to all requests + @override + void setRequestMetadata(String key, String value) { + _metaData[key] = value; + } + + ///For Android: Publish this BUO with Google app indexing so that the contents will be available with google search + ///For iOS: List items on Spotlight + @override + Future listOnSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + throw UnsupportedError('listOnSearch() Not supported by Branch JS SDK'); + } + + ///For Android: Remove the BUO from the local indexing if it is added to the local indexing already + /// This will remove the content from Google(Firebase) and other supported Indexing services + ///For iOS: Remove Branch Universal Object from Spotlight if privately indexed + @override + Future removeFromSearch( + {required BranchUniversalObject buo, + BranchLinkProperties? linkProperties}) async { + throw UnsupportedError('removeFromSearch() Not supported by Branch JS SDK'); + } + + ///Set time window for SKAdNetwork callouts in Hours (Only iOS) + ///By default, Branch limits calls to SKAdNetwork to within 72 hours after first install. + @override + void setIOSSKAdNetworkMaxTime(int hours) { + throw UnsupportedError( + 'setIOSSKAdNetworkMaxTime() Not available in Branch JS SDK'); + } + + ///Indicates whether or not this user has a custom identity specified for them. Note that this is independent of installs. + ///If you call setIdentity, this device will have that identity associated with this user until logout is called. + ///This includes persisting through uninstalls, as we track device id. + // NOTE: This is not really accurate for persistent checks... + @override + Future isUserIdentified() async { + return Future.value(_userIdentified); + } + + /// request AppTracking Autorization and return AppTrackingStatus + /// on Android returns notSupported + @override + Future requestTrackingAuthorization() async { + throw UnsupportedError( + 'requestTrackingAuthorization() Not available in Branch JS SDK'); + } + + /// return AppTrackingStatus + /// on Android returns notSupported + @override + Future getTrackingAuthorizationStatus() async { + throw UnsupportedError( + 'getTrackingAuthorizationStatus() Not available in Branch JS SDK'); + } + + /// return advertising identifier (ie tracking data). + /// on Android returns empty string + @override + Future getAdvertisingIdentifier() async { + throw UnsupportedError( + 'getAdvertisingIdentifier() Not available in Branch JS SDK'); + } + + ///Use the SDK integration validator to check that you've added the Branch SDK and + ///handle deep links correctly when you first integrate Branch into your app. + @override + void validateSDKIntegration() { + throw UnsupportedError( + 'validateSDKIntegration() not available in Branch JS SDK'); + } + + ///Sets the duration in milliseconds that the system should wait for initializing + ///a network * request. + @override + void setConnectTimeout(int connectTimeout) { + throw UnsupportedError( + 'setConnectTimeout() Not available in Branch JS SDK'); + } + + ///Sets the duration in milliseconds that the system should wait for a response + ///before timing out any Branch API. + ///Default 5500 ms. Note that this is the total time allocated for all request + ///retries as set in setRetryCount(int). + @override + void setTimeout(int timeout) { + throw UnsupportedError('setTimeout() Not available in Branch JS SDK'); + } + + ///Sets the max number of times to re-attempt a timed-out request to the Branch API, before + /// considering the request to have failed entirely. Default to 3. + /// Note that the the network timeout, as set in setNetworkTimeout(int), + /// together with the retry interval value from setRetryInterval(int) will + /// determine if the max retry count will be attempted. + @override + void setRetryCount(int retryCount) { + throw UnsupportedError('setRetryCount() Not available in Branch JS SDK'); + } + + ///Sets the amount of time in milliseconds to wait before re-attempting a + ///timed-out request to the Branch API. Default 1000 ms. + @override + void setRetryInterval(int retryInterval) { + throw UnsupportedError('setRetryInterval() Not available in Branch JS SDK'); + } + + ///Gets the available last attributed touch data with a custom set attribution window. + @override + Future getLastAttributedTouchData( + {int? attributionWindow}) async { + Completer responseCompleter = Completer(); + + try { + BranchJS.lastAttributedTouchData(attributionWindow, + allowInterop((err, data) { + if (err == null) { + if (data != null) { + responseCompleter.complete( + BranchResponse.success(result: _jsObjectToDartObject(data))); + } else { + responseCompleter.complete(BranchResponse.success(result: {})); + } + } else { + responseCompleter.complete(BranchResponse.error( + errorCode: '999', errorMessage: err.toString())); + } + })); + } catch (e) { + print(e); + debugPrint('getLastAttributedTouchData() error: ${e.toString()}'); + responseCompleter.complete(BranchResponse.error( + errorCode: '-1', errorMessage: 'getLastAttributedTouchData() error')); + } + return responseCompleter.future; + } + + ///Creates a Branch QR Code image. Returns the QR code as Uint8List. + @override + Future getQRCodeAsData( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + Completer responseCompleter = Completer(); + + Map data = buo.toMap(); + linkProperties.getControlParams().forEach((key, value) { + data[key] = value; + }); + + Map linkData = {...linkProperties.toMap(), 'data': data}; + + try { + BranchJS.qrCode(_dartObjectToJsObject(linkData), + _dartObjectToJsObject(qrCodeSettings.toMap()), + allowInterop((err, qrCode) { + if (err == null) { + if (qrCode != null) { + responseCompleter.complete( + BranchResponse.success(result: qrCode.rawBuffer.asUint8List())); + } else { + responseCompleter.complete(BranchResponse.error( + errorCode: '-1', errorMessage: 'Qrcode generate error')); + } + } else { + responseCompleter.complete(BranchResponse.error( + errorCode: '-1', errorMessage: err.toString())); + } + })); + } catch (e) { + responseCompleter.complete(BranchResponse.error( + errorCode: '-1', errorMessage: 'qrCode generate error')); + } + return responseCompleter.future; + } + + ///Creates a Branch QR Code image. Returns the QR code as a Image. + @override + Future getQRCodeAsImage( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required BranchQrCode qrCodeSettings}) async { + try { + BranchResponse response = await getQRCodeAsData( + buo: buo, + linkProperties: linkProperties, + qrCodeSettings: qrCodeSettings); + if (response.success) { + return BranchResponse.success( + result: Image.memory( + response.result, + )); + } else { + return BranchResponse.error( + errorCode: response.errorCode, errorMessage: response.errorMessage); + } + } catch (error) { + return BranchResponse.error( + errorCode: "-1", errorMessage: error.toString()); + } + } + + @override + void shareWithLPLinkMetadata( + {required BranchUniversalObject buo, + required BranchLinkProperties linkProperties, + required Uint8List icon, + required String title}) { + throw UnsupportedError( + 'shareWithLPLinkMetadata() Not available in Branch JS SDK'); + } + + void close() { + _initSessionStream.close(); + } +} diff --git a/lib/src/app_tracking_transparency.dart b/lib/src/objects/app_tracking_transparency.dart similarity index 96% rename from lib/src/app_tracking_transparency.dart rename to lib/src/objects/app_tracking_transparency.dart index d8bb0bdd..f615440a 100644 --- a/lib/src/app_tracking_transparency.dart +++ b/lib/src/objects/app_tracking_transparency.dart @@ -1,16 +1,16 @@ -enum AppTrackingStatus { - /// The user has not yet received an authorization request dialog - notDetermined, - - /// The device is restricted, tracking is disabled and the system can't show a request dialog - restricted, - - /// The user denies authorization for tracking - denied, - - /// The user authorizes access to tracking - authorized, - - /// The platform is not iOS or the iOS version is below 14.0 - notSupported, -} +enum AppTrackingStatus { + /// The user has not yet received an authorization request dialog + notDetermined, + + /// The device is restricted, tracking is disabled and the system can't show a request dialog + restricted, + + /// The user denies authorization for tracking + denied, + + /// The user authorizes access to tracking + authorized, + + /// The platform is not iOS or the iOS version is below 14.0 + notSupported, +} diff --git a/lib/src/objects/branch_event.dart b/lib/src/objects/branch_event.dart new file mode 100644 index 00000000..f9031307 --- /dev/null +++ b/lib/src/objects/branch_event.dart @@ -0,0 +1,131 @@ +part of flutter_branch_sdk_objects; +/* +* Enum for creating Branch events for tracking and analytical purpose. +* Enum class represent a standard or custom BranchEvents. Standard Branch events are defined with BRANCH_STANDARD_EVENT}. +* Please use #logEvent() method to log the events for tracking. +*/ + +enum BranchStandardEvent { + // Commerce events + ADD_TO_CART, + ADD_TO_WISHLIST, + VIEW_CART, + INITIATE_PURCHASE, + ADD_PAYMENT_INFO, + PURCHASE, + SPEND_CREDITS, + // Content Events + SEARCH, + VIEW_ITEM, + VIEW_ITEMS, + RATE, + SHARE, + // User Lifecycle Events + COMPLETE_REGISTRATION, + COMPLETE_TUTORIAL, + ACHIEVE_LEVEL, + UNLOCK_ACHIEVEMENT +} + +String getBranchStandardEventString(BranchStandardEvent branchStandardEvent) { + return branchStandardEvent.toString().split('.').last; +} + +enum BranchEventAdType { BANNER, INTERSTITIAL, REWARDED_VIDEO, NATIVE } + +String getBranchEventAdTypeString(BranchEventAdType branchEventAdType) { + return branchEventAdType.toString().split('.').last; +} + +class BranchEvent { + String _eventName = ''; + bool _isStandardEvent = true; + String transactionID = ''; + BranchCurrencyType? currency; + double revenue = -1; + double shipping = -1; + double tax = -1; + String coupon = ''; + String affiliation = ''; + String eventDescription = ''; + String searchQuery = ''; + BranchEventAdType? adType; + final Map _customData = {}; + + BranchEvent.standardEvent(BranchStandardEvent branchStandardEvent) { + _eventName = getBranchStandardEventString(branchStandardEvent); + _isStandardEvent = true; + } + + BranchEvent.customEvent(this._eventName) { + _isStandardEvent = false; + } + + String get eventName => _eventName; + bool get isStandardEvent => _isStandardEvent; + + void addCustomData(String key, dynamic value) { + _customData[key] = value; + } + + void removeCustomData(String key) { + _customData.remove(key); + } + + Map toMap() { + Map ret = {}; + + if (!kIsWeb) { + ret["eventName"] = _eventName; + ret["isStandardEvent"] = _isStandardEvent; + if (transactionID.isNotEmpty) { + ret["transactionID"] = transactionID; + } + if (currency != null) { + ret["currency"] = getCurrencyTypeString(currency!); + } + if (revenue != -1) ret["revenue"] = revenue; + if (shipping != -1) ret["shipping"] = shipping; + if (tax != -1) ret["tax"] = tax; + if (coupon.isNotEmpty) ret["coupon"] = coupon; + if (affiliation.isNotEmpty) ret["affiliation"] = affiliation; + if (eventDescription.isNotEmpty) { + ret["eventDescription"] = eventDescription; + } + if (searchQuery.isNotEmpty) { + ret["searchQuery"] = searchQuery; + } + if (adType != null) { + ret["adType"] = getBranchEventAdTypeString(adType!); + } + if (_customData.isNotEmpty) ret["customData"] = _customData; + } else { + if (_isStandardEvent) { + if (transactionID.isNotEmpty) { + ret["transactionID"] = transactionID; + } + if (currency != null) { + ret["currency"] = getCurrencyTypeString(currency!); + } + if (revenue != -1) ret["revenue"] = revenue; + if (shipping != -1) ret["shipping"] = shipping; + if (tax != -1) ret["tax"] = tax; + if (coupon.isNotEmpty) ret["coupon"] = coupon; + if (affiliation.isNotEmpty) ret["affiliation"] = affiliation; + if (eventDescription.isNotEmpty) { + ret["eventDescription"] = eventDescription; + } + if (searchQuery.isNotEmpty) { + ret["searchQuery"] = searchQuery; + } + if (adType != null) { + ret["adType"] = getBranchEventAdTypeString(adType!); + } + } + _customData.forEach((key, value) { + ret[key] = value; + }); + } + return ret; + } +} diff --git a/lib/src/objects/branch_qrcode.dart b/lib/src/objects/branch_qrcode.dart new file mode 100644 index 00000000..511bc187 --- /dev/null +++ b/lib/src/objects/branch_qrcode.dart @@ -0,0 +1,80 @@ +part of flutter_branch_sdk_objects; + +enum BranchImageFormat { + JPEG, + /* QR code is returned as a JPEG */ + PNG + /*QR code is returned as a PNG */ +} + +class BranchQrCode { + /* Primary color of the generated QR code itself. */ + Color? primaryColor; + /* Secondary color used as the QR Code background. */ + Color? backgroundColor; + /* The number of pixels for the QR code's border. Min 1px. Max 20px. */ + int? margin; + /* Output size of QR Code image. Min 300px. Max 2000px. */ + int? width; + /* A URL of an image that will be added to the center of the QR code. Must be a PNG or JPEG. */ + String centerLogoUrl; + /* Image Format of the returned QR code. Can be a JPEG or PNG. */ + BranchImageFormat imageFormat; + + BranchQrCode( + {this.primaryColor, + this.backgroundColor, + this.margin, + this.width, + this.imageFormat = BranchImageFormat.PNG, + this.centerLogoUrl = ''}) { + if (centerLogoUrl.isNotEmpty) { + assert(Uri.parse(centerLogoUrl).isAbsolute == true, 'Invalid URL'); + } + } + + Map toMap() { + Map ret = {}; + + if (!kIsWeb) { + if (primaryColor != null) { + ret["codeColor"] = _colorToHex(primaryColor!); + } + if (backgroundColor != null) { + ret["backgroundColor"] = _colorToHex(backgroundColor!); + } + if (margin != null) { + ret["margin"] = margin; + } + if (width != null) { + ret["width"] = width; + } + ret["imageFormat"] = imageFormat.name.toUpperCase(); + if (centerLogoUrl.isNotEmpty) { + ret["centerLogoUrl"] = centerLogoUrl; + } + } else { + if (primaryColor != null) { + ret["code_color"] = _colorToHex(primaryColor!); + } + if (backgroundColor != null) { + ret["background_color"] = _colorToHex(backgroundColor!); + } + if (margin != null) { + ret["margin"] = margin; + } + if (width != null) { + ret["width"] = width; + } + ret["image_format"] = imageFormat.name.toLowerCase(); + if (centerLogoUrl.isNotEmpty) { + ret["center_logo_url"] = centerLogoUrl; + } + } + return ret; + } + + String _colorToHex(Color color) { + return '#${color.value.toRadixString(16).substring(2, 8)}'; + } +} diff --git a/lib/src/branch_response.dart b/lib/src/objects/branch_response.dart similarity index 87% rename from lib/src/branch_response.dart rename to lib/src/objects/branch_response.dart index 3fe43ec1..215ccbad 100644 --- a/lib/src/branch_response.dart +++ b/lib/src/objects/branch_response.dart @@ -1,20 +1,20 @@ -part of flutter_branch_sdk_objects; - -class BranchResponse { - bool success = true; - T? result; - String errorCode = ''; - String errorMessage = ''; - - BranchResponse.success({required this.result}) { - this.success = true; - } - BranchResponse.error({required this.errorCode, required this.errorMessage}) { - this.success = false; - } - - @override - String toString() { - return ('success: $success, errorCode: $errorCode, errorMessage: $errorMessage}'); - } -} +part of flutter_branch_sdk_objects; + +class BranchResponse { + bool success = true; + T? result; + String errorCode = ''; + String errorMessage = ''; + + BranchResponse.success({required this.result}) { + success = true; + } + BranchResponse.error({required this.errorCode, required this.errorMessage}) { + success = false; + } + + @override + String toString() { + return ('success: $success, errorCode: $errorCode, errorMessage: $errorMessage}'); + } +} diff --git a/lib/src/branch_universal_object.dart b/lib/src/objects/branch_universal_object.dart similarity index 50% rename from lib/src/branch_universal_object.dart rename to lib/src/objects/branch_universal_object.dart index 9def5a10..a63beed4 100644 --- a/lib/src/branch_universal_object.dart +++ b/lib/src/objects/branch_universal_object.dart @@ -1,155 +1,159 @@ -library flutter_branch_sdk_objects; - -part 'branch_event.dart'; -part 'branch_response.dart'; -part 'content_meta_data.dart'; -part 'content_schema.dart'; -part 'link_properties.dart'; - -/* - * Class represents a single piece of content within your app, as well as any associated metadata. - * It provides convenient methods for sharing, deep linking, and tracking how often that content is viewed. This information is then used to provide you with powerful content analytics - * and deep linking. - */ -class BranchUniversalObject { - /* Canonical identifier for the content referred. */ - final String canonicalIdentifier; - - /* Canonical url for the content referred. This would be the corresponding website URL */ - String canonicalUrl = ''; - - /* Title for the content referred by BranchUniversalObject */ - String title = ''; - - /* Description for the content referred */ - String contentDescription = ''; - - /* An image url associated with the content referred */ - String imageUrl = ''; - - /* Meta data provided for the content. {@link ContentMetadata} object holds the metadata for this content */ - BranchContentMetaData? contentMetadata; - - /* Content index mode */ - bool publiclyIndex = true; - - /* Any keyword associated with the content. Used for indexing */ - List keywords; - - /* Expiry date for the content and any associated links. Represented as epoch milli second */ - int expirationDateInMilliSec = 0; - - /* Index mode for local content indexing */ - bool locallyIndex = true; - int _creationDateTimeStamp = DateTime.now().millisecondsSinceEpoch; - - ///Create a BranchUniversalObject with the given content. - BranchUniversalObject( - {required this.canonicalIdentifier, - this.canonicalUrl = '', - this.title = '', - this.contentDescription = '', - this.imageUrl = '', - this.contentMetadata, - this.keywords = const [], - this.publiclyIndex = true, - this.locallyIndex = true, - this.expirationDateInMilliSec = 0}); - - ///Adds any keywords associated with the content referred - void addKeyWords(List keywords) { - this.keywords.addAll(keywords); - } - - ///Add a keyword associated with the content referred - void addKeyWord(String keyword) { - this.keywords.add(keyword); - } - - ///Remove a keyword associated with the content referred - void removeKeyWord(String keyword) { - this.keywords.remove(keyword); - } - - ///Get the keywords associated with this BranchUniversalObject - List getKeywords() { - return this.keywords; - } - - Map toMap() { - Map ret = {}; - if (this.canonicalIdentifier.isNotEmpty) - ret["canonicalIdentifier"] = this.canonicalIdentifier; - - if (this.canonicalUrl.isNotEmpty) ret["canonicalUrl"] = this.canonicalUrl; - - if (this.title.isNotEmpty) ret["title"] = this.title; - - if (this.contentDescription.isNotEmpty) - ret["contentDescription"] = this.contentDescription; - - if (this.imageUrl.isNotEmpty) ret["imageUrl"] = this.imageUrl; - - if (this.keywords.isNotEmpty) ret["keywords"] = this.keywords; - - ret["creationDate"] = this._creationDateTimeStamp; - - if (this.expirationDateInMilliSec > 0) - ret["expirationDate"] = this.expirationDateInMilliSec; - - ret["locallyIndex"] = this.locallyIndex; - ret["publiclyIndex"] = this.publiclyIndex; - - if (this.contentMetadata != null && - this.contentMetadata!.toMap().isNotEmpty) - ret["contentMetadata"] = this.contentMetadata!.toMap(); - - if (ret.isEmpty) { - throw ArgumentError('Branch Universal Object is required'); - } - return ret; - } - - Map toMapWeb() { - Map ret = {}; - if (this.canonicalIdentifier.isNotEmpty) - ret["\$canonical_identifier"] = this.canonicalIdentifier; - - if (this.canonicalUrl.isNotEmpty) ret["\$canonicalUrl"] = this.canonicalUrl; - - if (this.title.isNotEmpty) ret["\$og_title"] = this.title; - - if (this.contentDescription.isNotEmpty) - ret["\$og_description"] = this.contentDescription; - - if (this.imageUrl.isNotEmpty) ret["\$og_image_url"] = this.imageUrl; - - if (this.keywords.isNotEmpty) ret["\$keywords"] = this.keywords; - - ret["\$creation_timestamp"] = this._creationDateTimeStamp; - - if (this.expirationDateInMilliSec > 0) - ret["\$exp_date"] = this.expirationDateInMilliSec; - - ret["\$locally_indexable"] = this.locallyIndex; - ret["\$publicly_indexable"] = this.publiclyIndex; - - Map contentMetadata = { - if (this.contentMetadata != null) ...this.contentMetadata!.toMapWeb() - }; - - if (contentMetadata.containsKey('customMetadata')) { - var customMetadata = contentMetadata['customMetadata']; - contentMetadata.remove('customMetadata'); - contentMetadata.addAll(customMetadata); - ret.addAll(contentMetadata); - } else { - ret.addAll(contentMetadata); - } - - if (ret.isEmpty) { - throw ArgumentError('Branch Universal Object is required'); - } - return ret; - } -} +library flutter_branch_sdk_objects; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +part 'branch_event.dart'; +part 'branch_qrcode.dart'; +part 'branch_response.dart'; +part 'content_meta_data.dart'; +part 'content_schema.dart'; +part 'link_properties.dart'; + +/* + * Class represents a single piece of content within your app, as well as any associated metadata. + * It provides convenient methods for sharing, deep linking, and tracking how often that content is viewed. This information is then used to provide you with powerful content analytics + * and deep linking. + */ +class BranchUniversalObject { + /* Canonical identifier for the content referred. */ + final String canonicalIdentifier; + + /* Canonical url for the content referred. This would be the corresponding website URL */ + String canonicalUrl = ''; + + /* Title for the content referred by BranchUniversalObject */ + String title = ''; + + /* Description for the content referred */ + String contentDescription = ''; + + /* An image url associated with the content referred */ + String imageUrl = ''; + + /* Meta data provided for the content. {@link ContentMetadata} object holds the metadata for this content */ + BranchContentMetaData? contentMetadata; + + /* Content index mode */ + bool publiclyIndex = true; + + /* Any keyword associated with the content. Used for indexing */ + List keywords; + + /* Expiry date for the content and any associated links. Represented as epoch milli second */ + int expirationDateInMilliSec = 0; + + /* Index mode for local content indexing */ + bool locallyIndex = true; + final int _creationDateTimeStamp = DateTime.now().millisecondsSinceEpoch; + + ///Create a BranchUniversalObject with the given content. + BranchUniversalObject( + {required this.canonicalIdentifier, + this.canonicalUrl = '', + this.title = '', + this.contentDescription = '', + this.imageUrl = '', + this.contentMetadata, + this.keywords = const [], + this.publiclyIndex = true, + this.locallyIndex = true, + this.expirationDateInMilliSec = 0}); + + ///Adds any keywords associated with the content referred + void addKeyWords(List keywords) { + keywords.addAll(keywords); + } + + ///Add a keyword associated with the content referred + void addKeyWord(String keyword) { + keywords.add(keyword); + } + + ///Remove a keyword associated with the content referred + void removeKeyWord(String keyword) { + keywords.remove(keyword); + } + + ///Get the keywords associated with this BranchUniversalObject + List getKeywords() { + return keywords; + } + + Map toMap() { + Map ret = {}; + if (!kIsWeb) { + if (canonicalIdentifier.isNotEmpty) { + ret["canonicalIdentifier"] = canonicalIdentifier; + } + + if (canonicalUrl.isNotEmpty) ret["canonicalUrl"] = canonicalUrl; + + if (title.isNotEmpty) ret["title"] = title; + + if (contentDescription.isNotEmpty) { + ret["contentDescription"] = contentDescription; + } + + if (imageUrl.isNotEmpty) ret["imageUrl"] = imageUrl; + + if (keywords.isNotEmpty) ret["keywords"] = keywords; + + ret["creationDate"] = _creationDateTimeStamp; + + if (expirationDateInMilliSec > 0) { + ret["expirationDate"] = expirationDateInMilliSec; + } + + ret["locallyIndex"] = locallyIndex; + ret["publiclyIndex"] = publiclyIndex; + + if (contentMetadata != null && contentMetadata!.toMap().isNotEmpty) { + ret["contentMetadata"] = contentMetadata!.toMap(); + } + } else { + if (canonicalIdentifier.isNotEmpty) { + ret["\$canonical_identifier"] = canonicalIdentifier; + } + + if (canonicalUrl.isNotEmpty) ret["\$canonicalUrl"] = canonicalUrl; + + if (title.isNotEmpty) ret["\$og_title"] = title; + + if (contentDescription.isNotEmpty) { + ret["\$og_description"] = contentDescription; + } + + if (imageUrl.isNotEmpty) ret["\$og_image_url"] = imageUrl; + + if (keywords.isNotEmpty) ret["\$keywords"] = keywords; + + ret["\$creation_timestamp"] = _creationDateTimeStamp; + + if (expirationDateInMilliSec > 0) { + ret["\$exp_date"] = expirationDateInMilliSec; + } + + ret["\$locally_indexable"] = locallyIndex; + ret["\$publicly_indexable"] = publiclyIndex; + + Map contentMetadata = { + if (this.contentMetadata != null) ...this.contentMetadata!.toMapWeb() + }; + + if (contentMetadata.containsKey('customMetadata')) { + var customMetadata = contentMetadata['customMetadata']; + contentMetadata.remove('customMetadata'); + contentMetadata.addAll(customMetadata); + ret.addAll(contentMetadata); + } else { + ret.addAll(contentMetadata); + } + } + + if (ret.isEmpty) { + throw ArgumentError('Branch Universal Object is required'); + } + return ret; + } +} diff --git a/lib/src/content_meta_data.dart b/lib/src/objects/content_meta_data.dart similarity index 61% rename from lib/src/content_meta_data.dart rename to lib/src/objects/content_meta_data.dart index 26955186..b232c0a6 100644 --- a/lib/src/content_meta_data.dart +++ b/lib/src/objects/content_meta_data.dart @@ -1,459 +1,460 @@ -part of flutter_branch_sdk_objects; - -enum BranchCondition { - OTHER, - NEW, - GOOD, - FAIR, - POOR, - USED, - REFURBISHED, - EXCELLENT -} - -enum BranchCurrencyType { - AED, - AFN, - ALL, - AMD, - ANG, - AOA, - ARS, - AUD, - AWG, - AZN, - BAM, - BBD, - BDT, - BGN, - BHD, - BIF, - BMD, - BND, - BOB, - BOV, - BRL, - BSD, - BTN, - BWP, - BYN, - BYR, - BZD, - CAD, - CDF, - CHE, - CHF, - CHW, - CLF, - CLP, - CNY, - COP, - COU, - CRC, - CUC, - CUP, - CVE, - CZK, - DJF, - DKK, - DOP, - DZD, - EGP, - ERN, - ETB, - EUR, - FJD, - FKP, - GBP, - GEL, - GHS, - GIP, - GMD, - GNF, - GTQ, - GYD, - HKD, - HNL, - HRK, - HTG, - HUF, - IDR, - ILS, - INR, - IQD, - IRR, - ISK, - JMD, - JOD, - JPY, - KES, - KGS, - KHR, - KMF, - KPW, - KRW, - KWD, - KYD, - KZT, - LAK, - LBP, - LKR, - LRD, - LSL, - LYD, - MAD, - MDL, - MGA, - MKD, - MMK, - MNT, - MOP, - MRO, - MUR, - MVR, - MWK, - MXN, - MXV, - MYR, - MZN, - NAD, - NGN, - NIO, - NOK, - NPR, - NZD, - OMR, - PAB, - PEN, - PGK, - PHP, - PKR, - PLN, - PYG, - QAR, - RON, - RSD, - RUB, - RWF, - SAR, - SBD, - SCR, - SDG, - SEK, - SGD, - SHP, - SLL, - SOS, - SRD, - SSP, - STD, - SYP, - SZL, - THB, - TJS, - TMT, - TND, - TOP, - TRY, - TTD, - TWD, - TZS, - UAH, - UGX, - USD, - USN, - UYI, - UYU, - UZS, - VEF, - VND, - VUV, - WST, - XAF, - XAG, - XAU, - XBA, - XBB, - XBC, - XBD, - XCD, - XDR, - XFU, - XOF, - XPD, - XPF, - XPT, - XSU, - XTS, - XUA, - XXX, - YER, - ZAR, - ZMW -} - -String getCurrencyTypeString(BranchCurrencyType currencyType) { - return currencyType.toString().split('.').last; -} - -enum BranchProductCategory { - ANIMALS_AND_PET_SUPPLIES, - APPAREL_AND_ACCESSORIES, - ARTS_AND_ENTERTAINMENT, - BABY_AND_TODDLER, - BUSINESS_AND_INDUSTRIAL, - CAMERAS_AND_OPTICS, - ELECTRONICS, - FOOD_BEVERAGES_AND_TOBACCO, - FURNITURE, - HARDWARE, - HEALTH_AND_BEAUTY, - HOME_AND_GARDEN, - LUGGAGE_AND_BAGS, - MATURE, - MEDIA, - OFFICE_SUPPLIES, - RELIGIOUS_AND_CEREMONIAL, - SOFTWARE, - SPORTING_GOODS, - TOYS_AND_GAMES, - VEHICLES_AND_PARTS, -} - -/* -Class for describing metadata for a piece of content represented by a FlutterBranchUniversalObject -*/ -class BranchContentMetaData { - /// Schema for the qualifying content item. Please see [BranchContentSchema] - BranchContentSchema? contentSchema; - - /// Quantity of the thing associated with the qualifying content item - double quantity = 0; - - /// Any price associated with the qualifying content item - double price = 0; - - /// Currency type associated with the price - BranchCurrencyType? currencyType; - - /// Holds any associated store keeping unit - String sku = ''; - - /// Name of any product specified by this metadata - String productName = ''; - - /// Any brand name associated with this metadata - String productBrand = ''; - - /// Category of product if this metadata is for a product - /// Value should be one of the enumeration from {@link ProductCategory} - BranchProductCategory? productCategory; - - /// Condition of the product item. Value is one of the enum constants from {@link CONDITION} - BranchCondition? condition; - - /// Variant of product if this metadata is for a product - String productVariant = ''; - - /// Rating for the qualifying content item - double rating = 0; - - /// Average rating for the qualifying content item - double ratingAverage = 0; - - /// Total number of ratings for the qualifying content item - int ratingCount = 0; - - ///Maximum ratings for the qualifying content item - double ratingMax = 0; - - /// Street address associated with the qualifying content item - String _addressStreet = ''; - - /// City name associated with the qualifying content item - String _addressCity = ''; - - /// Region or province name associated with the qualifying content item - String _addressRegion = ''; - - /// Country name associated with the qualifying content item - String _addressCountry = ''; - - /// Postal code associated with the qualifying content item - String _addressPostalCode = ''; - - /// Latitude value associated with the qualifying content item - double? _latitude; - - /// Latitude value associated with the qualifying content item - double? _longitude; - - List _imageCaptions = const []; - Map _customMetadata = {}; - - String? _getProductConditionString(BranchCondition? productCondition) { - if (productCondition == null) return null; - return productCondition.toString().split('.').last; - } - - String? _getProductCategoryString(BranchProductCategory? productCategory) { - if (productCategory == null) return null; - switch (productCategory) { - case BranchProductCategory.ANIMALS_AND_PET_SUPPLIES: - return "Animals & Pet Supplies"; - case BranchProductCategory.APPAREL_AND_ACCESSORIES: - return "Apparel & Accessories"; - case BranchProductCategory.ARTS_AND_ENTERTAINMENT: - return "Arts & Entertainment"; - case BranchProductCategory.BABY_AND_TODDLER: - return "Baby & Toddler"; - case BranchProductCategory.BUSINESS_AND_INDUSTRIAL: - return "Business & Industrial"; - case BranchProductCategory.CAMERAS_AND_OPTICS: - return "Cameras & Optics"; - case BranchProductCategory.ELECTRONICS: - return "Electronics"; - case BranchProductCategory.FOOD_BEVERAGES_AND_TOBACCO: - return "Food, Beverages & Tobacco"; - case BranchProductCategory.FURNITURE: - return "Furniture"; - case BranchProductCategory.HARDWARE: - return "Hardware"; - case BranchProductCategory.HEALTH_AND_BEAUTY: - return "Health & Beauty"; - case BranchProductCategory.HOME_AND_GARDEN: - return "Home & Garden"; - case BranchProductCategory.LUGGAGE_AND_BAGS: - return "Luggage & Bags"; - case BranchProductCategory.MATURE: - return "Mature"; - case BranchProductCategory.MEDIA: - return "Media"; - case BranchProductCategory.OFFICE_SUPPLIES: - return "Office Supplies"; - case BranchProductCategory.RELIGIOUS_AND_CEREMONIAL: - return "Religious & Ceremonial"; - case BranchProductCategory.SOFTWARE: - return "Software"; - case BranchProductCategory.SPORTING_GOODS: - return "Sporting Goods"; - case BranchProductCategory.TOYS_AND_GAMES: - return "Toys & Games"; - case BranchProductCategory.VEHICLES_AND_PARTS: - return "Vehicles & Parts"; - default: - return "Home & Garden"; - } - } - - BranchContentMetaData addImageCaptions(List captions) { - this._imageCaptions = captions; - return this; - } - - BranchContentMetaData addCustomMetadata(String key, dynamic value) { - this._customMetadata[key] = value; - return this; - } - - BranchContentMetaData setAddress( - {String? street, - String? city, - String? region, - String? country, - String? postalCode}) { - if (street != null) this._addressStreet = street; - if (city != null) this._addressCity = city; - if (region != null) this._addressRegion = region; - if (country != null) this._addressCountry = country; - if (postalCode != null) this._addressPostalCode = postalCode; - return this; - } - - BranchContentMetaData setLocation(double latitude, double longitude) { - this._latitude = latitude; - this._longitude = longitude; - return this; - } - - Map toMap() { - Map ret = Map(); - if (this.contentSchema != null) - ret["content_schema"] = getContentSchemaString(this.contentSchema); - if (this.quantity > 0) ret["quantity"] = this.quantity; - if (this.price > 0) ret["price"] = this.price; - if (this.currencyType != null) - ret["currency"] = getCurrencyTypeString(this.currencyType!); - if (this.sku.isNotEmpty) ret["sku"] = this.sku; - if (this.productName.isNotEmpty) ret["product_name"] = this.productName; - if (this.productBrand.isNotEmpty) ret["product_brand"] = this.productBrand; - if (this.productCategory != null) - ret["product_category"] = _getProductCategoryString(this.productCategory); - if (this.productVariant.isNotEmpty) - ret["product_variant"] = this.productVariant; - if (this.condition != null) - ret["condition"] = _getProductConditionString(this.condition); - if (this.ratingAverage > 0) ret["rating_average"] = this.ratingAverage; - if (this.ratingCount > 0) ret["rating_count"] = this.ratingCount; - if (this.ratingMax > 0) ret["rating_max"] = this.ratingMax; - if (this.rating > 0) ret["rating"] = this.rating; - if (this._addressStreet.isNotEmpty) - ret["address_street"] = this._addressStreet; - if (this._addressCity.isNotEmpty) ret["address_city"] = this._addressCity; - if (this._addressRegion.isNotEmpty) - ret["address_region"] = this._addressRegion; - if (this._addressCountry.isNotEmpty) - ret["address_country"] = this._addressCountry; - if (this._addressPostalCode.isNotEmpty) - ret["address_postal_code"] = this._addressPostalCode; - if (this._latitude != null) ret["latitude"] = this._latitude; - if (this._longitude != null) ret["longitude"] = this._longitude; - if (_imageCaptions.isNotEmpty) ret["image_captions"] = this._imageCaptions; - if (this._customMetadata.isNotEmpty) { - ret["customMetadata"] = this._customMetadata; - } - return ret; - } - - Map toMapWeb() { - Map ret = Map(); - if (this.contentSchema != null) - ret["\$content_schema"] = getContentSchemaString(this.contentSchema); - if (this.quantity > 0) ret["\$quantity"] = this.quantity; - if (this.price > 0) ret["\$price"] = this.price; - if (this.currencyType != null) - ret["\$currency"] = getCurrencyTypeString(this.currencyType!); - if (this.sku.isNotEmpty) ret["\$sku"] = this.sku; - if (this.productName.isNotEmpty) ret["\$product_name"] = this.productName; - if (this.productBrand.isNotEmpty) - ret["\$product_brand"] = this.productBrand; - if (this.productCategory != null) - ret["\$product_category"] = - this.productCategory.toString().split('.').last; - if (this.productVariant.isNotEmpty) - ret["\$product_variant"] = this.productVariant; - if (this.condition != null) - ret["\$condition"] = _getProductConditionString(this.condition); - if (this.ratingAverage > 0) ret["\$rating_average"] = this.ratingAverage; - if (this.ratingCount > 0) ret["\$rating_count"] = this.ratingCount; - if (this.ratingMax > 0) ret["\$rating_max"] = this.ratingMax; - if (this.rating > 0) ret["\$rating"] = this.rating; - if (this._addressStreet.isNotEmpty) - ret["\$address_street"] = this._addressStreet; - if (this._addressCity.isNotEmpty) ret["\$address_city"] = this._addressCity; - if (this._addressRegion.isNotEmpty) - ret["\$address_region"] = this._addressRegion; - if (this._addressCountry.isNotEmpty) - ret["\$address_country"] = this._addressCountry; - if (this._addressPostalCode.isNotEmpty) - ret["\$address_postal_code"] = this._addressPostalCode; - if (this._latitude != null) ret["\$latitude"] = this._latitude; - if (this._longitude != null) ret["\$longitude"] = this._longitude; - if (_imageCaptions.isNotEmpty) - ret["\$image_captions"] = this._imageCaptions; - this._customMetadata.forEach((key, value) { - ret['$key'] = value; - }); - return ret; - } -} +part of flutter_branch_sdk_objects; + +enum BranchCondition { + OTHER, + NEW, + GOOD, + FAIR, + POOR, + USED, + REFURBISHED, + EXCELLENT +} + +enum BranchCurrencyType { + AED, + AFN, + ALL, + AMD, + ANG, + AOA, + ARS, + AUD, + AWG, + AZN, + BAM, + BBD, + BDT, + BGN, + BHD, + BIF, + BMD, + BND, + BOB, + BOV, + BRL, + BSD, + BTN, + BWP, + BYN, + BYR, + BZD, + CAD, + CDF, + CHE, + CHF, + CHW, + CLF, + CLP, + CNY, + COP, + COU, + CRC, + CUC, + CUP, + CVE, + CZK, + DJF, + DKK, + DOP, + DZD, + EGP, + ERN, + ETB, + EUR, + FJD, + FKP, + GBP, + GEL, + GHS, + GIP, + GMD, + GNF, + GTQ, + GYD, + HKD, + HNL, + HRK, + HTG, + HUF, + IDR, + ILS, + INR, + IQD, + IRR, + ISK, + JMD, + JOD, + JPY, + KES, + KGS, + KHR, + KMF, + KPW, + KRW, + KWD, + KYD, + KZT, + LAK, + LBP, + LKR, + LRD, + LSL, + LYD, + MAD, + MDL, + MGA, + MKD, + MMK, + MNT, + MOP, + MRO, + MUR, + MVR, + MWK, + MXN, + MXV, + MYR, + MZN, + NAD, + NGN, + NIO, + NOK, + NPR, + NZD, + OMR, + PAB, + PEN, + PGK, + PHP, + PKR, + PLN, + PYG, + QAR, + RON, + RSD, + RUB, + RWF, + SAR, + SBD, + SCR, + SDG, + SEK, + SGD, + SHP, + SLL, + SOS, + SRD, + SSP, + STD, + SYP, + SZL, + THB, + TJS, + TMT, + TND, + TOP, + TRY, + TTD, + TWD, + TZS, + UAH, + UGX, + USD, + USN, + UYI, + UYU, + UZS, + VEF, + VND, + VUV, + WST, + XAF, + XAG, + XAU, + XBA, + XBB, + XBC, + XBD, + XCD, + XDR, + XFU, + XOF, + XPD, + XPF, + XPT, + XSU, + XTS, + XUA, + XXX, + YER, + ZAR, + ZMW +} + +String getCurrencyTypeString(BranchCurrencyType currencyType) { + return currencyType.toString().split('.').last; +} + +enum BranchProductCategory { + ANIMALS_AND_PET_SUPPLIES, + APPAREL_AND_ACCESSORIES, + ARTS_AND_ENTERTAINMENT, + BABY_AND_TODDLER, + BUSINESS_AND_INDUSTRIAL, + CAMERAS_AND_OPTICS, + ELECTRONICS, + FOOD_BEVERAGES_AND_TOBACCO, + FURNITURE, + HARDWARE, + HEALTH_AND_BEAUTY, + HOME_AND_GARDEN, + LUGGAGE_AND_BAGS, + MATURE, + MEDIA, + OFFICE_SUPPLIES, + RELIGIOUS_AND_CEREMONIAL, + SOFTWARE, + SPORTING_GOODS, + TOYS_AND_GAMES, + VEHICLES_AND_PARTS, +} + +/* +Class for describing metadata for a piece of content represented by a FlutterBranchUniversalObject +*/ +class BranchContentMetaData { + /// Schema for the qualifying content item. Please see [BranchContentSchema] + BranchContentSchema? contentSchema; + + /// Quantity of the thing associated with the qualifying content item + double quantity = 0; + + /// Any price associated with the qualifying content item + double price = 0; + + /// Currency type associated with the price + BranchCurrencyType? currencyType; + + /// Holds any associated store keeping unit + String sku = ''; + + /// Name of any product specified by this metadata + String productName = ''; + + /// Any brand name associated with this metadata + String productBrand = ''; + + /// Category of product if this metadata is for a product + /// Value should be one of the enumeration from {@link ProductCategory} + BranchProductCategory? productCategory; + + /// Condition of the product item. Value is one of the enum constants from {@link CONDITION} + BranchCondition? condition; + + /// Variant of product if this metadata is for a product + String productVariant = ''; + + /// Rating for the qualifying content item + double rating = 0; + + /// Average rating for the qualifying content item + double ratingAverage = 0; + + /// Total number of ratings for the qualifying content item + int ratingCount = 0; + + ///Maximum ratings for the qualifying content item + double ratingMax = 0; + + /// Street address associated with the qualifying content item + String _addressStreet = ''; + + /// City name associated with the qualifying content item + String _addressCity = ''; + + /// Region or province name associated with the qualifying content item + String _addressRegion = ''; + + /// Country name associated with the qualifying content item + String _addressCountry = ''; + + /// Postal code associated with the qualifying content item + String _addressPostalCode = ''; + + /// Latitude value associated with the qualifying content item + double? _latitude; + + /// Latitude value associated with the qualifying content item + double? _longitude; + + List _imageCaptions = const []; + final Map _customMetadata = {}; + + String? _getProductConditionString(BranchCondition? productCondition) { + if (productCondition == null) return null; + return productCondition.toString().split('.').last; + } + + String? _getProductCategoryString(BranchProductCategory? productCategory) { + if (productCategory == null) return null; + switch (productCategory) { + case BranchProductCategory.ANIMALS_AND_PET_SUPPLIES: + return "Animals & Pet Supplies"; + case BranchProductCategory.APPAREL_AND_ACCESSORIES: + return "Apparel & Accessories"; + case BranchProductCategory.ARTS_AND_ENTERTAINMENT: + return "Arts & Entertainment"; + case BranchProductCategory.BABY_AND_TODDLER: + return "Baby & Toddler"; + case BranchProductCategory.BUSINESS_AND_INDUSTRIAL: + return "Business & Industrial"; + case BranchProductCategory.CAMERAS_AND_OPTICS: + return "Cameras & Optics"; + case BranchProductCategory.ELECTRONICS: + return "Electronics"; + case BranchProductCategory.FOOD_BEVERAGES_AND_TOBACCO: + return "Food, Beverages & Tobacco"; + case BranchProductCategory.FURNITURE: + return "Furniture"; + case BranchProductCategory.HARDWARE: + return "Hardware"; + case BranchProductCategory.HEALTH_AND_BEAUTY: + return "Health & Beauty"; + case BranchProductCategory.HOME_AND_GARDEN: + return "Home & Garden"; + case BranchProductCategory.LUGGAGE_AND_BAGS: + return "Luggage & Bags"; + case BranchProductCategory.MATURE: + return "Mature"; + case BranchProductCategory.MEDIA: + return "Media"; + case BranchProductCategory.OFFICE_SUPPLIES: + return "Office Supplies"; + case BranchProductCategory.RELIGIOUS_AND_CEREMONIAL: + return "Religious & Ceremonial"; + case BranchProductCategory.SOFTWARE: + return "Software"; + case BranchProductCategory.SPORTING_GOODS: + return "Sporting Goods"; + case BranchProductCategory.TOYS_AND_GAMES: + return "Toys & Games"; + case BranchProductCategory.VEHICLES_AND_PARTS: + return "Vehicles & Parts"; + default: + return "Home & Garden"; + } + } + + BranchContentMetaData addImageCaptions(List captions) { + _imageCaptions = captions; + return this; + } + + BranchContentMetaData addCustomMetadata(String key, dynamic value) { + _customMetadata[key] = value; + return this; + } + + BranchContentMetaData setAddress( + {String? street, + String? city, + String? region, + String? country, + String? postalCode}) { + if (street != null) _addressStreet = street; + if (city != null) _addressCity = city; + if (region != null) _addressRegion = region; + if (country != null) _addressCountry = country; + if (postalCode != null) _addressPostalCode = postalCode; + return this; + } + + BranchContentMetaData setLocation(double latitude, double longitude) { + _latitude = latitude; + _longitude = longitude; + return this; + } + + Map toMap() { + Map ret = {}; + if (contentSchema != null) { + ret["content_schema"] = getContentSchemaString(contentSchema); + } + if (quantity > 0) ret["quantity"] = quantity; + if (price > 0) ret["price"] = price; + if (currencyType != null) { + ret["currency"] = getCurrencyTypeString(currencyType!); + } + if (sku.isNotEmpty) ret["sku"] = sku; + if (productName.isNotEmpty) ret["product_name"] = productName; + if (productBrand.isNotEmpty) ret["product_brand"] = productBrand; + if (productCategory != null) { + ret["product_category"] = _getProductCategoryString(productCategory); + } + if (productVariant.isNotEmpty) ret["product_variant"] = productVariant; + if (condition != null) { + ret["condition"] = _getProductConditionString(condition); + } + if (ratingAverage > 0) ret["rating_average"] = ratingAverage; + if (ratingCount > 0) ret["rating_count"] = ratingCount; + if (ratingMax > 0) ret["rating_max"] = ratingMax; + if (rating > 0) ret["rating"] = rating; + if (_addressStreet.isNotEmpty) ret["address_street"] = _addressStreet; + if (_addressCity.isNotEmpty) ret["address_city"] = _addressCity; + if (_addressRegion.isNotEmpty) ret["address_region"] = _addressRegion; + if (_addressCountry.isNotEmpty) ret["address_country"] = _addressCountry; + if (_addressPostalCode.isNotEmpty) { + ret["address_postal_code"] = _addressPostalCode; + } + if (_latitude != null) ret["latitude"] = _latitude; + if (_longitude != null) ret["longitude"] = _longitude; + if (_imageCaptions.isNotEmpty) ret["image_captions"] = _imageCaptions; + if (_customMetadata.isNotEmpty) { + ret["customMetadata"] = _customMetadata; + } + return ret; + } + + Map toMapWeb() { + Map ret = {}; + if (contentSchema != null) { + ret["\$content_schema"] = getContentSchemaString(contentSchema); + } + if (quantity > 0) ret["\$quantity"] = quantity; + if (price > 0) ret["\$price"] = price; + if (currencyType != null) { + ret["\$currency"] = getCurrencyTypeString(currencyType!); + } + if (sku.isNotEmpty) ret["\$sku"] = sku; + if (productName.isNotEmpty) ret["\$product_name"] = productName; + if (productBrand.isNotEmpty) ret["\$product_brand"] = productBrand; + if (productCategory != null) { + ret["\$product_category"] = productCategory.toString().split('.').last; + } + if (productVariant.isNotEmpty) ret["\$product_variant"] = productVariant; + if (condition != null) { + ret["\$condition"] = _getProductConditionString(condition); + } + if (ratingAverage > 0) ret["\$rating_average"] = ratingAverage; + if (ratingCount > 0) ret["\$rating_count"] = ratingCount; + if (ratingMax > 0) ret["\$rating_max"] = ratingMax; + if (rating > 0) ret["\$rating"] = rating; + if (_addressStreet.isNotEmpty) ret["\$address_street"] = _addressStreet; + if (_addressCity.isNotEmpty) ret["\$address_city"] = _addressCity; + if (_addressRegion.isNotEmpty) ret["\$address_region"] = _addressRegion; + if (_addressCountry.isNotEmpty) ret["\$address_country"] = _addressCountry; + if (_addressPostalCode.isNotEmpty) { + ret["\$address_postal_code"] = _addressPostalCode; + } + if (_latitude != null) ret["\$latitude"] = _latitude; + if (_longitude != null) ret["\$longitude"] = _longitude; + if (_imageCaptions.isNotEmpty) { + ret["\$image_captions"] = _imageCaptions; + } + _customMetadata.forEach((key, value) { + ret[key] = value; + }); + return ret; + } +} diff --git a/lib/src/content_schema.dart b/lib/src/objects/content_schema.dart similarity index 95% rename from lib/src/content_schema.dart rename to lib/src/objects/content_schema.dart index d3e747f5..1c536289 100644 --- a/lib/src/content_schema.dart +++ b/lib/src/objects/content_schema.dart @@ -1,44 +1,44 @@ -part of flutter_branch_sdk_objects; - -enum BranchContentSchema { - COMMERCE_AUCTION, - COMMERCE_BUSINESS, - COMMERCE_OTHER, - COMMERCE_PRODUCT, - COMMERCE_RESTAURANT, - COMMERCE_SERVICE, - COMMERCE_TRAVEL_FLIGHT, - COMMERCE_TRAVEL_HOTEL, - COMMERCE_TRAVEL_OTHER, - GAME_STATE, - MEDIA_IMAGE, - MEDIA_MIXED, - MEDIA_MUSIC, - MEDIA_OTHER, - MEDIA_VIDEO, - OTHER, - TEXT_ARTICLE, - TEXT_BLOG, - TEXT_OTHER, - TEXT_RECIPE, - TEXT_REVIEW, - TEXT_SEARCH_RESULTS, - TEXT_STORY, - TEXT_TECHNICAL_DOC -} - -BranchContentSchema getValueContentSchema(String name) { - BranchContentSchema? schema; - for (BranchContentSchema contentSchema in BranchContentSchema.values) { - if (contentSchema.toString() == name) { - schema = contentSchema; - break; - } - } - return schema!; -} - -String? getContentSchemaString(BranchContentSchema? contentSchema) { - if (contentSchema == null) return null; - return contentSchema.toString().split('.').last; -} +part of flutter_branch_sdk_objects; + +enum BranchContentSchema { + COMMERCE_AUCTION, + COMMERCE_BUSINESS, + COMMERCE_OTHER, + COMMERCE_PRODUCT, + COMMERCE_RESTAURANT, + COMMERCE_SERVICE, + COMMERCE_TRAVEL_FLIGHT, + COMMERCE_TRAVEL_HOTEL, + COMMERCE_TRAVEL_OTHER, + GAME_STATE, + MEDIA_IMAGE, + MEDIA_MIXED, + MEDIA_MUSIC, + MEDIA_OTHER, + MEDIA_VIDEO, + OTHER, + TEXT_ARTICLE, + TEXT_BLOG, + TEXT_OTHER, + TEXT_RECIPE, + TEXT_REVIEW, + TEXT_SEARCH_RESULTS, + TEXT_STORY, + TEXT_TECHNICAL_DOC +} + +BranchContentSchema getValueContentSchema(String name) { + BranchContentSchema? schema; + for (BranchContentSchema contentSchema in BranchContentSchema.values) { + if (contentSchema.toString() == name) { + schema = contentSchema; + break; + } + } + return schema!; +} + +String? getContentSchemaString(BranchContentSchema? contentSchema) { + if (contentSchema == null) return null; + return contentSchema.toString().split('.').last; +} diff --git a/lib/src/link_properties.dart b/lib/src/objects/link_properties.dart similarity index 66% rename from lib/src/link_properties.dart rename to lib/src/objects/link_properties.dart index d8f01b63..b84faf05 100644 --- a/lib/src/link_properties.dart +++ b/lib/src/objects/link_properties.dart @@ -1,76 +1,60 @@ -part of flutter_branch_sdk_objects; -/* -* Class for representing any additional information that is specific to the link. -* Use this class to specify the properties of a deep link such as channel, feature etc and any control params associated with the link. -* -*/ - -class BranchLinkProperties { - List tags = const []; - final String feature; - final String alias; - final String stage; - final int matchDuration; - Map _controlParams = {}; - final String channel; - final String campaign; - - BranchLinkProperties( - {this.channel = '', - this.feature = '', - this.alias = '', - this.matchDuration = 0, - this.stage = '', - this.tags = const [], - this.campaign = ''}); - - void addTags(String tag) { - tags.add(tag); - } - - List getTags() { - return this.tags; - } - - Map getControlParams() { - return this._controlParams; - } - - BranchLinkProperties addControlParam(String key, dynamic value) { - this._controlParams[key] = value; - return this; - } - - Map toMap() { - Map ret = {}; - - if (tags.length > 0) ret['tags'] = tags; - if (feature.isNotEmpty) ret['feature'] = feature; - if (alias.isNotEmpty) ret['alias'] = alias; - if (stage.isNotEmpty) ret['stage'] = stage; - if (matchDuration > 0) ret['matchDuration'] = matchDuration; - if (_controlParams.isNotEmpty) ret['controlParams'] = _controlParams; - if (channel.isNotEmpty) ret['channel'] = channel; - if (campaign.isNotEmpty) ret['campaign'] = campaign; - if (ret.isEmpty) { - throw ArgumentError('Link Properties is required'); - } - return ret; - } - - Map toMapWeb() { - Map ret = {}; - - if (tags.length > 0) ret['tags'] = tags; - if (feature.isNotEmpty) ret['feature'] = feature; - if (alias.isNotEmpty) ret['alias'] = alias; - if (stage.isNotEmpty) ret['stage'] = stage; - if (matchDuration > 0) ret['matchDuration'] = matchDuration; - if (channel.isNotEmpty) ret['channel'] = channel; - if (campaign.isNotEmpty) ret['campaign'] = campaign; - if (ret.isEmpty) { - throw ArgumentError('Link Properties is required'); - } - return ret; - } -} +part of flutter_branch_sdk_objects; +/* +* Class for representing any additional information that is specific to the link. +* Use this class to specify the properties of a deep link such as channel, feature etc and any control params associated with the link. +* +*/ + +class BranchLinkProperties { + List tags = const []; + final String feature; + final String alias; + final String stage; + final int matchDuration; + final Map _controlParams = {}; + final String channel; + final String campaign; + + BranchLinkProperties( + {this.channel = '', + this.feature = '', + this.alias = '', + this.matchDuration = 0, + this.stage = '', + this.tags = const [], + this.campaign = ''}); + + void addTags(String tag) { + tags.add(tag); + } + + List getTags() { + return tags; + } + + Map getControlParams() { + return _controlParams; + } + + BranchLinkProperties addControlParam(String key, dynamic value) { + _controlParams[key] = value; + return this; + } + + Map toMap() { + Map ret = {}; + + if (tags.isNotEmpty) ret['tags'] = tags; + if (feature.isNotEmpty) ret['feature'] = feature; + if (alias.isNotEmpty) ret['alias'] = alias; + if (stage.isNotEmpty) ret['stage'] = stage; + if (matchDuration > 0) ret['matchDuration'] = matchDuration; + if (_controlParams.isNotEmpty) ret['controlParams'] = _controlParams; + if (channel.isNotEmpty) ret['channel'] = channel; + if (campaign.isNotEmpty) ret['campaign'] = campaign; + if (ret.isEmpty) { + throw ArgumentError('Link Properties is required'); + } + return ret; + } +} diff --git a/lib/src/web/branch_js.dart b/lib/src/web/branch_js.dart index 4acafc72..cc0c64d9 100644 --- a/lib/src/web/branch_js.dart +++ b/lib/src/web/branch_js.dart @@ -1,956 +1,901 @@ -@JS() -library branchjs; - -// ignore: avoid_web_libraries_in_flutter -import 'dart:js'; - -import 'package:js/js.dart'; - -@JS('JSON.stringify') -external String jsonStringify(Object obj); - -@JS('JSON.parse') -external dynamic jsonParse(String str); - -@JS('navigator.share') -external dynamic navigatorShare(Object data); - -@JS('prompt') -external dynamic browserPrompt(String message, [String data]); - -@JS('branch') -class BranchJS { - /// addListener(event, listener) - /// Parameters - /// - /// event: String, optional - Specify which events you would like to listen for. If - /// not defined, the observer will recieve all events. - /// - /// listener: function, required - Listening function that will recieves an - /// event as a string and optional data as an object. - /// - /// The Branch Web SDK includes a simple event listener, that currently only publishes events for - /// Journeys events. - /// Future development will include the ability to subscribe to events related to all other Web - /// SDK functionality. - /// - /// Example - /// - /// var listener = function(event, data) { console.log(event, data); } - /// - /// // Specify an event to listen for - /// branch.addListener('willShowJourney', listener); - /// - /// // Listen for all events - /// branch.addListener(listener); - /// Available Journey Events: - /// - /// willShowJourney: Journey is about to be shown. - /// didShowJourney: Journey's entrance animation has completed and it is being shown to the user. - /// willNotShowJourney: Journey will not be shown and no other events will be emitted. - /// didClickJourneyCTA: User clicked on Journey's CTA button. - /// didClickJourneyClose: User clicked on Journey's close button. - /// willCloseJourney: Journey close animation has started. - /// didCloseJourney: Journey's close animation has completed and it is no longer visible to the user. - /// didCallJourneyClose: Emitted when developer calls branch.closeJourney() to dismiss Journey. - @JS('addListener') - external static void addListener([String event, Function listener]); - - // Some internal method not documented - // @JS('applyCode') - // external static void applyCode(); - - /// autoAppIndex(data, callback) - /// Parameters - /// - /// data: Object, optional - Information on how to build your App Indexing tags for your webpage - /// - /// callback: function, optional - Returns an error string if unsuccessful - /// - /// This function generates and inserts Firebase App Indexing tags between the section of your webpage. - /// Once inserted, these tags will help Google index and surface content from your App in Google Search. - /// - /// Listed below are optional parameters which can be used to build your page's App Indexing Tags: - /// - /// Key Value - /// "androidPackageName" Android App's package name - /// "androidURL" A custom scheme for your Android App such as: example/home/cupertino/12345 where example is the App's URI scheme and home/cupertino/12345 routes to unique content in the App - /// "iosAppId" iTunes App Store ID for your iOS App - /// "iosURL" A custom scheme for your iOS App such as: example/home/cupertino/12345 - /// "data" Any additional deep link data that you would like to pass to your App - /// Resultant Firebase App Indexing tags will have the following format: - /// - /// Text - /// Text - /// JavaScript - /// - /// - /// Example - /// - /// branch.autoAppIndex({ - /// iosAppId:'123456789', - /// iosURL:'example/home/cupertino/12345', - /// androidPackageName:'com.somecompany.app', - /// androidURL:'example/home/cupertino/12345', - /// data:{"walkScore":65, "transitScore":50} - /// }, function(err) { console.log(err); }); - @JS('autoAppIndex') - external static void autoAppIndex([data, Function callback]); - - // No documentation in full reference - // @JS('banner') - // external static void banner(); - - // No documentation in full reference - // @JS('closeBanner') - // external static void closeBanner(); - - /// closeJourney(callback) - /// Parameters - /// - /// callback: function, optional - /// - /// Journeys include a close button the user can click, but you may want to close the - /// Journey with a timeout, or via some other user interaction with your web app. In this case, - /// closing the Journey is very simple by calling Branch.closeJourney(). - /// - /// Usage - /// - /// branch.closeJourney(function(err) { console.log(err); }); - @JS('closeJourney') - external static void closeJourney([Function callback]); - - /// creditHistory(options, callback) - /// Parameters - /// - /// options: Object, optional - options controlling the returned history - /// - /// callback: function, required - returns an array with credit history - /// data - /// - /// This call will retrieve the entire history of credits and redemptions from the individual user. - /// Properties available in the options object: - /// - /// Key Value - /// bucket optional (max 63 characters) - The bucket from which to retrieve credit transactions. - /// begin_after_id optional - The credit transaction id of the last item in the previous retrieval. Retrieval will start from the transaction next to it. If none is specified, retrieval starts from the very beginning in the transaction history, depending on the order. - /// length optional - The number of credit transactions to retrieve. If none is specified, up to 100 credit transactions will be retrieved. - /// direction DEPRECATED - The order of credit transactions to retrieve. If direction is 1, retrieval is in least recent first order; If direction is 0, or if none is specified, retrieval is in most recent first order. No longer supported. - /// Usage - /// - /// branch.creditHistory( - /// options, - /// callback(err, data) - /// ); - /// Example - /// - /// branch.creditHistory( - /// { - /// "length":50, - /// "direction":0, // no longer supported. - /// "begin_after_id":"123456789012345", - /// "bucket":"default" - /// } - /// callback (err, data) - /// ); - /// Callback Format - /// - /// callback( - /// "Error message", - /// [ - /// { - /// "transaction": { - /// "date": "2014-10-14T01:54:40.425Z", - /// "id": "50388077461373184", - /// "bucket": "default", - /// "type": 0, - /// "amount": 5 - /// }, - /// "referrer": "12345678", - /// "referree": null - /// }, - /// { - /// "transaction": { - /// "date": "2014-10-14T01:55:09.474Z", - /// "id": "50388199301710081", - /// "bucket": "default", - /// "type": 2, - /// "amount": -3 - /// }, - /// "referrer": null, - /// "referree": "12345678" - /// } - /// ] - /// ); - @JS('creditHistory') - external static void creditHistory([options, Function callback]); - - /// credits(callback) - /// Parameters - /// - /// callback: function, required - returns an object with credit data. - /// - /// Formerly showCredits()] - /// - /// This call will retrieve the entire history of credits and redemptions from the individual user. - /// - /// Usage - /// - /// branch.credits( - /// callback (err, data) - /// ); - /// Callback Format - /// - /// callback( - /// "Error message", - /// { - /// 'default': 15, - /// 'other bucket': 9 - /// } - /// ); - @JS('credits') - external static void credits(Function callback); - - /// data(callback) - /// Parameters - /// - /// callback: function, optional - callback to read the - /// session data. - /// - /// Returns the same session information and any referring data, as - /// Branch.init, but does not require the app_id. This is meant to be called - /// after Branch.init has been called if you need the session information at a - /// later point. - /// If the Branch session has already been initialized, the callback will return - /// immediately, otherwise, it will return once Branch has been initialized. - @JS('data') - external static void data([Function callback]); - - /// deepview(data, options, callback) - /// Parameters - /// - /// data: Object, required - object of all link data, same as branch.link(). - /// - /// options: Object, optional - { make_new_link: whether to create a new link even if - /// one already exists. open_app, whether to try to open the app passively (as opposed to - /// opening it upon user clicking); defaults to true - /// }. - /// - /// callback: function, optional - returns an error if the API call is unsuccessful - /// - /// Turns the current page into a "deepview" – a preview of app content. This gives the page two - /// special behaviors: - /// - /// When the page is viewed on a mobile browser, if the user has the app - /// installed on their phone, we will try to open the app automaticaly and deeplink them to this content (this can be toggled off by turning open_app to false, but this is not recommended). - /// Provides a callback to open the app directly, accessible as branch.deepviewCta(); - /// you'll want to have a button on your web page that says something like "View in app", which calls this function. - /// See this tutorial for a full - /// guide on how to use the deepview functionality of the Web SDK. - /// - /// Usage - /// - /// branch.deepview( - /// data, - /// options, - /// callback (err) - /// ); - /// Example - /// - /// branch.deepview( - /// { - /// channel: 'facebook', - /// data: { - /// mydata: 'content of my data', - /// foo: 'bar', - /// '$deeplink_path': 'item_id=12345' - /// }, - /// feature: 'dashboard', - /// stage: 'new user', - /// tags: [ 'tag1', 'tag2' ], - /// }, - /// { - /// make_new_link: true, - /// open_app: true - /// }, - /// function(err) { - /// console.log(err || 'no error'); - /// } - /// ); - /// Callback Format - /// - /// callback( - /// "Error message" - /// ); - @JS('deepview') - external static void deepview(Object data, - [Object options, Function callback]); - - /// deepviewCta() - /// Perform the branch deepview CTA (call to action) on mobile after branch.deepview() call is - /// finished. If the branch.deepview() call is finished with no error, when branch.deepviewCta() is called, - /// an attempt is made to open the app and deeplink the end user into it; if the end user does not - /// have the app installed, they will be redirected to the platform-appropriate app stores. If on the - /// other hand, branch.deepview() returns with an error, branch.deepviewCta() will fall back to - /// redirect the user using - /// Branch dynamic links. - /// - /// If branch.deepview() has not been called, an error will arise with a reminder to call - /// branch.deepview() first. - /// - /// Usage - /// - /// $('a.deepview-cta').click(branch.deepviewCta); // If you are using jQuery - /// - /// document.getElementById('my-elem').onClick = branch.deepviewCta; // Or generally - /// - /// // In HTML - /// - /// // We recommend to assign deepviewCta in deepview callback: - /// branch.deepview(data, option, function(err) { - /// if (err) { - /// throw err; - /// } - /// $('a.deepview-cta').click(branch.deepviewCta); - /// }); - /// - /// // You can call this function any time after branch.deepview() is finished by simply: - /// branch.deepviewCta(); - /// - /// When debugging, please call branch.deepviewCta() with an error callback like so: - /// - /// branch.deepviewCta(function(err) { - /// if (err) { - /// console.log(err); - /// } - /// }); - /// Referral System Rewarding Functionality - /// In a standard referral system, you have 2 parties: the original user and the invitee. Our system - /// is flexible enough to handle rewards for all users for any actions. Here are a couple example - /// scenarios: - /// - /// Reward the original user for taking action (eg. inviting, purchasing, etc) - /// Reward the invitee for installing the app from the original user's referral link - /// Reward the original user when the invitee takes action (eg. give the original user credit when - /// their the invitee buys something) - /// These reward definitions are created on the dashboard, under the 'Reward Rules' section in the - /// 'Referrals' tab on the dashboard. - /// - /// Warning: For a referral program, you should not use unique awards for custom events and redeem - /// pre-identify call. This can allow users to cheat the system. - @JS('deepviewCta') - external static void deepviewCta([Function errorCallback]); - - /// first(callback) - /// Parameters - /// - /// callback: function, optional - callback to read the - /// session data. - /// - /// Returns the same session information and any referring data, as - /// Branch.init did when the app was first installed. This is meant to be called - /// after Branch.init has been called if you need the first session information at a - /// later point. - /// If the Branch session has already been initialized, the callback will return - /// immediately, otherwise, it will return once Branch has been initialized. - @JS('first') - external static void first([Function callback]); - - // No documentation on reference - // @JS('getCode') - // external static void getCode(); - - /// init(branch_key, options, callback) - /// Parameters - /// - /// branch_key: string, required - Your Branch live key, or (deprecated) your app id. - /// - /// options: Object, optional - { }. - /// - /// callback: function, optional - callback to read the - /// session data. - /// - /// Adding the Branch script to your page automatically creates a window.branch - /// object with all the external methods described below. All calls made to - /// Branch methods are stored in a queue, so even if the SDK is not fully - /// instantiated, calls made to it will be queued in the order they were - /// originally called. - /// If the session was opened from a referring link, data() will also return the referring link - /// click as referring_link, which gives you the ability to continue the click flow. - /// - /// The init function on the Branch object initiates the Branch session and - /// creates a new user session, if it doesn't already exist, in - /// sessionStorage. - /// - /// Useful Tip: The init function returns a data object where you can read - /// the link the user was referred by. - /// - /// Properties available in the options object: - /// - /// Key Value - /// branch_match_id optional - string. The current user's browser-fingerprint-id. The value of this parameter should be the same as the value of ?branch_match_id (automatically appended by Branch after a link click). _Only necessary if ?_branch_match_id is lost due to multiple redirects in your flow. - /// branch_view_id optional - string. If you would like to test how Journeys render on your page before activating them, you can set the value of this parameter to the id of the view you are testing. Only necessary when testing a view related to a Journey. - /// no_journeys optional - boolean. When true, prevents Journeys from appearing on current page. - /// disable_entry_animation optional - boolean. When true, prevents a Journeys entry animation. - /// disable_exit_animation optional - boolean. When true, prevents a Journeys exit animation. - /// retries optional - integer. Value specifying the number of times that a Branch API call can be re-attempted. Default 2. - /// retry_delay optional - integer . Amount of time in milliseconds to wait before re-attempting a timed-out request to the Branch API. Default 200 ms. - /// timeout optional - integer. Duration in milliseconds that the system should wait for a response before considering any Branch API call to have timed out. Default 5000 ms. - /// metadata optional - object. Key-value pairs used to target Journeys users via the "is viewing a page with metadata key" filter. - /// nonce optional - string. A nonce value that will be added to branch-journey-cta injected script. Used to allow that script from a Content Security Policy. - /// tracking_disabled optional - boolean. true disables tracking - /// Usage - /// - /// branch.init( - /// branch_key, - /// options, - /// callback (err, data), - /// ); - /// Callback Format - /// - /// callback( - /// "Error message", - /// { - /// data_parsed: { }, // If the user was referred from a link, and the link has associated data, the data is passed in here. - /// referring_identity: '12345', // If the user was referred from a link, and the link was created by a user with an identity, that identity is here. - /// has_app: true, // Does the user have the app installed already? - /// identity: 'BranchUser', // Unique string that identifies the user - /// ~referring_link: 'https://bnc.lt/c/jgg75-Gjd3' // The referring link click, if available. - /// } - /// ); - /// Note: Branch.init must be called prior to calling any other Branch functions. - @JS('init') - external static void init(String branchKey, - [Object? options, Function? callback]); - - /// link(data, callback) - /// Parameters - /// - /// data: Object, required - link data and metadata. - /// - /// callback: function, required - returns a string of the Branch deep - /// linking URL. - /// - /// Formerly createLink() - /// - /// Creates and returns a deep linking URL. The data parameter can include an - /// object with optional data you would like to store, including Facebook - /// Open Graph data. - /// - /// data The dictionary to embed with the link. Accessed as session or install parameters from - /// the SDK. - /// - /// Note - /// You can customize the Facebook OG tags of each URL if you want to dynamically share content by - /// using the following optional keys in the data dictionary. Please use this - /// Facebook tool to debug your OG tags! - /// - /// Key Value - /// "$og_title" The title you'd like to appear for the link in social media - /// "$og_description" The description you'd like to appear for the link in social media - /// "$og_image_url" The URL for the image you'd like to appear for the link in social media - /// "$og_video" The URL for the video - /// "$og_url" The URL you'd like to appear - /// "$og_redirect" If you want to bypass our OG tags and use your own, use this key with the URL that contains your site's metadata. - /// Also, you can set custom redirection by inserting the following optional keys in the dictionary: - /// - /// Key Value - /// "$desktop_url" Where to send the user on a desktop or laptop. By default it is the Branch-hosted text-me service - /// "$android_url" The replacement URL for the Play Store to send the user if they don't have the app. Only necessary if you want a mobile web splash - /// "$ios_url" The replacement URL for the App Store to send the user if they don't have the app. Only necessary if you want a mobile web splash - /// "$ipad_url" Same as above but for iPad Store - /// "$fire_url" Same as above but for Amazon Fire Store - /// "$blackberry_url" Same as above but for Blackberry Store - /// "$windows_phone_url" Same as above but for Windows Store - /// "$after_click_url" When a user returns to the browser after going to the app, take them to this URL. iOS only; Android coming soon - /// You have the ability to control the direct deep linking of each link as well: - /// - /// Key Value - /// "$deeplink_path" The value of the deep link path that you'd like us to append to your URI. For example, you could specify "$deeplink_path": "radio/station/456" and we'll open the app with the URI "yourapp://radio/station/456?link_click_id=branch-identifier". This is primarily for supporting legacy deep linking infrastructure. - /// "$always_deeplink" true or false. (default is not to deep link first) This key can be specified to have our linking service force try to open the app, even if we're not sure the user has the app installed. If the app is not installed, we fall back to the respective app store or $platform_url key. By default, we only open the app if we've seen a user initiate a session in your app from a Branch link (has been cookied and deep linked by Branch). - /// Usage - /// - /// branch.link( - /// data, - /// callback (err, link) - /// ); - /// Example - /// - /// branch.link({ - /// tags: [ 'tag1', 'tag2' ], - /// channel: 'facebook', - /// feature: 'dashboard', - /// stage: 'new user', - /// data: { - /// mydata: 'something', - /// foo: 'bar', - /// '$desktop_url': 'http://myappwebsite.com', - /// '$ios_url': 'http://myappwebsite.com/ios', - /// '$ipad_url': 'http://myappwebsite.com/ipad', - /// '$android_url': 'http://myappwebsite.com/android', - /// '$og_app_id': '12345', - /// '$og_title': 'My App', - /// '$og_description': 'My app\'s description.', - /// '$og_image_url': 'http://myappwebsite.com/image.png' - /// } - /// }, function(err, link) { - /// console.log(err, link); - /// }); - /// Callback Format - /// - /// callback( - /// "Error message", - /// 'https://bnc.lt/l/3HZMytU-BW' // Branch deep linking URL - /// ); - @JS('link') - external static void link(Object data, Function callback); - - /// logout(callback) - /// Parameters - /// - /// callback: function, optional - /// - /// Logs out the current session, replaces session IDs and identity IDs. - /// - /// Usage - /// - /// branch.logout( - /// callback (err) - /// ); - /// Callback Format - /// - /// callback( - /// "Error message" - /// ); - @JS('logout') - external static void logout([Function callback]); - - /// redeem(amount, bucket, callback) - /// Parameters - /// - /// amount: number, required - an amount (int) of number of credits to redeem - /// - /// bucket: string, required - the name of the bucket (string) of which bucket to redeem the credits from - /// - /// callback: function, optional - returns an error if unsuccessful - /// - /// Formerly redeemCredits()] - /// - /// Credits are stored in buckets, which you can define as points, currency, whatever makes sense - /// for your app. When you want to redeem credits, call this method with the number of points to be - /// redeemed, and the bucket to redeem them from. - /// - /// branch.redeem( - /// amount, // Amount of credits to be redeemed - /// bucket, // String of bucket name to redeem credits from - /// callback (err) - /// ); - /// Example - /// - /// branch.redeem( - /// 5, - /// "Rubies", - /// function(err) { - /// console.log(err); - /// } - /// ); - /// Callback Format - /// - /// callback("Error message"); - @JS('redeem') - external static void redeem(int amount, String bucket, [Function callback]); - - // No documentation on reference - // @JS('referrals') - // external static void referrals(); - - /// removeListener(listener) - /// Parameters - /// - /// listener: function, required - Reference to the listening function you - /// would like to remove. note: this must be the same reference that was passed to - /// branch.addListener(), not an identical clone of the function. - /// - /// Remove the listener from observations, if it is present. Not that this function must be - /// passed a referrence to the same function that was passed to branch.addListener(), not - /// just an identical clone of the function. - @JS('removeListener') - external static void removeListener(Function listener); - - /// sendSMS(phone, linkData, options, callback) - /// Parameters - /// - /// phone: string, required - phone number to send SMS to - /// - /// linkData: Object, required - object of link data - /// - /// options: Object, optional - options: make_new_link, which forces the creation of a - /// new link even if one already exists - /// - /// callback: function, optional - Returns an error if unsuccessful - /// - /// Formerly SMSLink() - /// - /// A robust function to give your users the ability to share links via SMS. If - /// the user navigated to this page via a Branch link, sendSMS will send that - /// same link. Otherwise, it will create a new link with the data provided in - /// the params argument. sendSMS also registers a click event with the - /// channel pre-filled with 'sms' before sending an sms to the provided - /// phone parameter. This way the entire link click event is recorded starting - /// with the user sending an sms. - /// - /// Note: sendSMS will automatically send a previously generated link click, - /// along with the data object in the original link. Therefore, it is unneccessary for the - /// data() method to be called to check for an already existing link. If a link already - /// exists, sendSMS will simply ignore the data object passed to it, and send the existing link. - /// If this behavior is not desired, set make_new_link: true in the options object argument - /// of sendSMS, and sendSMS will always make a new link. - /// - /// Supports international SMS. - /// - /// Please note that the destination phone number needs to be from the same country the SMS is being sent from. - /// - /// Usage - /// - /// branch.sendSMS( - /// phone, - /// linkData, - /// options, - /// callback (err, data) - /// ); - /// Example - /// - /// var linkData = { - /// tags: ['tag1', 'tag2'], - /// channel: 'Website', - /// feature: 'TextMeTheApp', - /// data: { - /// // here's how to define the deeplink_path and the custom text on the front end - /// $deeplink_path: `custom_deeplink_path`, - /// //for the custom text, use {{link}} as a macro for link location - /// $custom_sms_text: `Here's my custom text, and here is the {{ link }}`, - /// mydata: 'something', - /// foo: 'bar', - /// '$desktop_url': 'http://myappwebsite.com', - /// '$ios_url': 'http://myappwebsite.com/ios', - /// '$ipad_url': 'http://myappwebsite.com/ipad', - /// '$android_url': 'http://myappwebsite.com/android', - /// '$og_app_id': '12345', - /// '$og_title': 'My App', - /// '$og_description': 'My app\'s description.', - /// '$og_image_url': 'http://myappwebsite.com/image.png' - /// } - /// } - /// branch.sendSMS( - /// '9999999999', - /// linkData, - /// { make_new_link: false }, // Default: false. If set to true, sendSMS will generate a new link even if one already exists. - /// function(err) { console.log(err); } - /// ); - /// Callback Format - /// - /// callback("Error message"); - @JS('sendSMS') - external static void sendSMS(String phone, Object linkData, - [Object options, Function callback]); - - /// setBranchViewData(data) - /// Parameters - /// - /// data: Object, required - object of all link data, same as Branch.link() - /// - /// This function lets you set the deep link data dynamically for a given mobile web Journey. For - /// example, if you desgin a full page interstitial, and want the deep link data to be custom for each - /// page, you'd need to use this function to dynamically set the deep link params on page load. Then, - /// any Journey loaded on that page will inherit these deep link params. - /// - /// Usage - /// - /// branch.setBranchViewData( - /// data // Data for link, same as Branch.link() - /// ); - /// Example - /// - /// branch.setBranchViewData({ - /// tags: ['tag1', 'tag2'], - /// data: { - /// mydata: 'something', - /// foo: 'bar', - /// '$deeplink_path': 'open/item/1234' - /// } - /// }); - @JS('setBranchViewData') - external static void setBranchViewData(Object data); - - /// setIdentity(identity, callback) - /// Parameters - /// - /// identity: string, required - a string uniquely identifying the user - often a user ID - /// or email address. - /// - /// callback: function, optional - callback that returns the user's - /// Branch identity id and unique link. - /// - /// Formerly identify() - /// - /// Sets the identity of a user and returns the data. To use this function, pass - /// a unique string that identifies the user - this could be an email address, - /// UUID, Facebook ID, etc. - /// - /// Usage - /// - /// branch.setIdentity( - /// identity, - /// callback (err, data) - /// ); - /// Callback Format - /// - /// callback( - /// "Error message", - /// { - /// identity_id: '12345', // Server-generated ID of the user identity, stored in `sessionStorage`. - /// link: 'url', // New link to use (replaces old stored link), stored in `sessionStorage`. - /// referring_data_parsed: { }, // Returns the initial referring data for this identity, if exists, as a parsed object. - /// referring_identity: '12345' // Returns the initial referring identity for this identity, if exists. - /// } - /// ); - @JS('setIdentity') - external static void setIdentity(String identity, [Function callback]); - - /// track(event, metadata, callback) - /// Parameters - /// - /// event: string, required - name of the event to be tracked. - /// - /// metadata: Object, optional - object of event metadata. - /// - /// callback: function, optional - /// - /// This function allows you to track any event with supporting metadata. - /// The metadata parameter is a formatted JSON object that can contain - /// any data and has limitless hierarchy - /// - /// Usage - /// - /// branch.track( - /// event, - /// metadata, - /// callback (err) - /// ); - /// Callback Format - /// - /// callback("Error message"); - @JS('track') - external static void track(String event, - [Object metadata, Function callback]); - - // No documentation in reference - // @JS('validateCode') - // external static void validateCode(); - - /// trackCommerceEvent(event, commerce_data, metadata, callback) - /// Parameters - /// - /// event: String, required - Name of the commerce event to be tracked. We currently support 'purchase' events - /// - /// commerce_data: Object, required - Data that describes the commerce event - /// - /// metadata: Object, optional - metadata you may want add to the event - /// - /// callback: function, optional - Returns an error if unsuccessful - /// - /// Sends a user commerce event to the server - /// - /// Use commerce events to track when a user purchases an item in your online store, - /// makes an in-app purchase, or buys a subscription. The commerce events are tracked in - /// the Branch dashboard along with your other events so you can judge the effectiveness of - /// campaigns and other analytics. - /// - /// Usage - /// - /// branch.trackCommerceEvent( - /// event, - /// commerce_data, - /// metadata, - /// callback (err) - /// ); - /// Example - /// - /// var commerce_data = { - /// "revenue": 50.0, - /// "currency": "USD", - /// "transaction_id": "foo-transaction-id", - /// "shipping": 0.0, - /// "tax": 5.0, - /// "affiliation": "foo-affiliation", - /// "products": [ - /// { "sku": "foo-sku-1", "name": "foo-item-1", "price": 45.00, "quantity": 1, "brand": "foo-brand", - /// "category": "Electronics", "variant": "foo-variant-1"}, - /// { "sku": "foo-sku-2", "price": 2.50, "quantity": 2} - /// ], - /// }; - /// - /// var metadata = { "foo": "bar" }; - /// - /// branch.trackCommerceEvent('purchase', commerce_data, metadata, function(err) { - /// if(err) { - /// throw err; - /// } - /// }); - @JS('trackCommerceEvent') - external static void trackCommerceEvent(String name, Object commerceData, - [Object metadata, Function callback]); - - /// logEvent(event, event_data_and_custom_data, content_items, customer_event_alias, callback) - /// Parameters - /// - /// event: String, required - /// - /// event_data_and_custom_data: Object, optional - /// - /// content_items: Array, optional - /// - /// customer_event_alias: String, optional - /// - /// callback: function, optional - /// - /// Register commerce events, content events, user lifecycle events and custom events via logEvent() - /// - /// NOTE: If this is the first time you are integrating our new event tracking feature via logEvent(), please use the latest Branch WebSDK snippet from the Installation section. This has been updated in v2.30.0 of our SDK. - /// - /// The guides below provide information about what keys can be sent when triggering these event types: - /// - /// Logging Commerce Events - /// Logging Content Events - /// Logging User Lifecycle - /// Logging Custom Events - /// Usage for Commerce, Content & User Lifecycle "Standard Events" - /// - /// branch.logEvent( - /// event, - /// event_data_and_custom_data, - /// content_items, - /// customer_event_alias, - /// callback (err) - /// ); - /// Usage for "Custom Events" - /// - /// JavaScript - /// JavaScript - /// branch.logEvent( - /// event, - /// custom_data, - /// callback (err) - /// ); - /// `` - /// **Notes**: - /// - logEvent() sends user_data automatically - /// - When firing Standard Events, send custom and event data as part of the same object - /// - Custom Events do not contain content items and event data - /// - /// ### Example -- How to log a Commerce Event - /// var event_and_custom_data = { - /// "transaction_id": "tras_Id_1232343434", - /// "currency": "USD", - /// "revenue": 180.2, - /// "shipping": 10.5, - /// "tax": 13.5, - /// "coupon": "promo-1234", - /// "affiliation": "high_fi", - /// "description": "Preferred purchase", - /// "purchase_loc": "Palo Alto", - /// "store_pickup": "unavailable" - /// }; - /// var content_items = [ - /// { - /// "$content_schema": "COMMERCE_PRODUCT", - /// "$og_title": "Nike Shoe", - /// "$og_description": "Start loving your steps", - /// "$og_image_url": "http:///example.com/img1.jpg", - /// "$canonical_identifier": "nike/1234", - /// "$publicly_indexable": false, - /// "$price": 101.2, - /// "$locally_indexable": true, - /// "$quantity": 1, - /// "$sku": "1101123445", - /// "$product_name": "Runner", - /// "$product_brand": "Nike", - /// "$product_category": "Sporting Goods", - /// "$product_variant": "XL", - /// "$rating_average": 4.2, - /// "$rating_count": 5, - /// "$rating_max": 2.2, - /// "$creation_timestamp": 1499892854966, - /// "$exp_date": 1499892854966, - /// "$keywords": [ "sneakers", "shoes" ], - /// "$address_street": "230 South LaSalle Street", - /// "$address_city": "Chicago", - /// "$address_region": "IL", - /// "$address_country": "US", - /// "$address_postal_code": "60604", - /// "$latitude": 12.07, - /// "$longitude": -97.5, - /// "$image_captions": [ "my_img_caption1", "my_img_caption_2" ], - /// "$condition": "NEW", - /// "$custom_fields": {"foo1":"bar1","foo2":"bar2"} - /// }, - /// { - /// "$og_title": "Nike Woolen Sox", - /// "$canonical_identifier": "nike/5324", - /// "$og_description": "Fine combed woolen sox for those who love your foot", - /// "$publicly_indexable": false, - /// "$price": 80.2, - /// "$locally_indexable": true, - /// "$quantity": 5, - /// "$sku": "110112467", - /// "$product_name": "Woolen Sox", - /// "$product_brand": "Nike", - /// "$product_category": "Apparel & Accessories", - /// "$product_variant": "Xl", - /// "$rating_average": 3.3, - /// "$rating_count": 5, - /// "$rating_max": 2.8, - /// "$creation_timestamp": 1499892854966 - /// }]; - /// var customer_event_alias = "event alias"; - /// branch.logEvent( - /// "PURCHASE", - /// event_and_custom_data, - /// content_items, - /// customer_event_alias, - /// function(err) { console.log(err); } - /// ); - - @JS('logEvent') - external static void logEvent(String event, - [Object eventDataAndCustomData, - JsArray contentItems, - String customerEventAlias, - Function callback]); - - /// disableTracking(disableTracking) - /// Parameters - /// - /// disableTracking: Boolean, optional - true disables tracking and false re-enables tracking. - /// - /// Notes: - /// - /// disableTracking() without a parameter is a shorthand for disableTracking(true). - /// If a call to disableTracking(false) is made, the WebSDK will re-initialize. Additionally, if tracking_disabled: true is passed - /// as an option to init(), it will be removed during the reinitialization process. - /// Allows User to Remain Private - /// - /// This will prevent any Branch requests from being sent across the network, except for the case of deep linking. - /// If someone clicks a Branch link, but has expressed not to be tracked, we will return deep linking data back to the - /// client but without tracking information. - /// - /// In do-not-track mode, you will still be able to create links and display Journeys however, they will not have identifiable - /// information associated to them. You can change this behavior at any time, by calling the aforementioned function. - /// The do-not-track mode state is persistent: it is saved for the user across browser sessions for the web site. - @JS('disableTracking') - external static void disableTracking([bool disableTracking]); - - @JS('lastAttributedTouchData') - external static void lastAttributedTouchData(attributionWindow, - [Function callback]); -} +@JS() +library branchjs; + +// ignore: avoid_web_libraries_in_flutter +import 'dart:js'; +import 'dart:typed_data'; + +import 'package:js/js.dart'; + +@JS('JSON.stringify') +external String jsonStringify(Object obj); + +@JS('JSON.parse') +external dynamic jsonParse(String str); + +@JS('navigator.share') +external dynamic navigatorShare(Object data); + +@JS('prompt') +external dynamic browserPrompt(String message, [String data]); + +@JS() +@anonymous +class QrCodeData { + external ByteBuffer rawBuffer; + external Function base64(); +} + +@JS('branch') +class BranchJS { + /// addListener(event, listener) + /// Parameters + /// + /// event: String, optional - Specify which events you would like to listen for. If + /// not defined, the observer will recieve all events. + /// + /// listener: function, required - Listening function that will recieves an + /// event as a string and optional data as an object. + /// + /// The Branch Web SDK includes a simple event listener, that currently only publishes events for + /// Journeys events. + /// Future development will include the ability to subscribe to events related to all other Web + /// SDK functionality. + /// + /// Example + /// + /// var listener = function(event, data) { console.log(event, data); } + /// + /// // Specify an event to listen for + /// branch.addListener('willShowJourney', listener); + /// + /// // Listen for all events + /// branch.addListener(listener); + /// Available Journey Events: + /// + /// willShowJourney: Journey is about to be shown. + /// didShowJourney: Journey's entrance animation has completed and it is being shown to the user. + /// willNotShowJourney: Journey will not be shown and no other events will be emitted. + /// didClickJourneyCTA: User clicked on Journey's CTA button. + /// didClickJourneyClose: User clicked on Journey's close button. + /// willCloseJourney: Journey close animation has started. + /// didCloseJourney: Journey's close animation has completed and it is no longer visible to the user. + /// didCallJourneyClose: Emitted when developer calls branch.closeJourney() to dismiss Journey. + @JS('addListener') + external static void addListener([String event, Function listener]); + + // No documentation in full reference + // @JS('banner') + // external static void banner(); + + // No documentation in full reference + // @JS('closeBanner') + // external static void closeBanner(); + + /// closeJourney(callback) + /// Parameters + /// + /// callback: function, optional + /// + /// Journeys include a close button the user can click, but you may want to close the + /// Journey with a timeout, or via some other user interaction with your web app. In this case, + /// closing the Journey is very simple by calling Branch.closeJourney(). + /// + /// Usage + /// + /// branch.closeJourney(function(err) { console.log(err); }); + @JS('closeJourney') + external static void closeJourney([Function callback]); + + /// data(callback) + /// Parameters + /// + /// callback: function, optional - callback to read the + /// session data. + /// + /// Returns the same session information and any referring data, as + /// Branch.init, but does not require the app_id. This is meant to be called + /// after Branch.init has been called if you need the session information at a + /// later point. + /// If the Branch session has already been initialized, the callback will return + /// immediately, otherwise, it will return once Branch has been initialized. + @JS('data') + external static void data([Function callback]); + + /// deepview(data, options, callback) + /// Parameters + /// + /// data: Object, required - object of all link data, same as branch.link(). + /// + /// options: Object, optional - { make_new_link: whether to create a new link even if + /// one already exists. open_app, whether to try to open the app passively (as opposed to + /// opening it upon user clicking); defaults to true + /// }. + /// + /// callback: function, optional - returns an error if the API call is unsuccessful + /// + /// Turns the current page into a "deepview" – a preview of app content. This gives the page two + /// special behaviors: + /// + /// When the page is viewed on a mobile browser, if the user has the app + /// installed on their phone, we will try to open the app automaticaly and deeplink them to this content (this can be toggled off by turning open_app to false, but this is not recommended). + /// Provides a callback to open the app directly, accessible as branch.deepviewCta(); + /// you'll want to have a button on your web page that says something like "View in app", which calls this function. + /// See this tutorial for a full + /// guide on how to use the deepview functionality of the Web SDK. + /// + /// Usage + /// + /// branch.deepview( + /// data, + /// options, + /// callback (err) + /// ); + /// Example + /// + /// branch.deepview( + /// { + /// channel: 'facebook', + /// data: { + /// mydata: 'content of my data', + /// foo: 'bar', + /// '$deeplink_path': 'item_id=12345' + /// }, + /// feature: 'dashboard', + /// stage: 'new user', + /// tags: [ 'tag1', 'tag2' ], + /// }, + /// { + /// make_new_link: true, + /// open_app: true + /// }, + /// function(err) { + /// console.log(err || 'no error'); + /// } + /// ); + /// Callback Format + /// + /// callback( + /// "Error message" + /// ); + @JS('deepview') + external static void deepview(Object data, + [Object options, Function callback]); + + /// deepviewCta() + /// Perform the branch deepview CTA (call to action) on mobile after branch.deepview() call is + /// finished. If the branch.deepview() call is finished with no error, when branch.deepviewCta() is called, + /// an attempt is made to open the app and deeplink the end user into it; if the end user does not + /// have the app installed, they will be redirected to the platform-appropriate app stores. If on the + /// other hand, branch.deepview() returns with an error, branch.deepviewCta() will fall back to + /// redirect the user using + /// Branch dynamic links. + /// + /// If branch.deepview() has not been called, an error will arise with a reminder to call + /// branch.deepview() first. + /// + /// Usage + /// + /// $('a.deepview-cta').click(branch.deepviewCta); // If you are using jQuery + /// + /// document.getElementById('my-elem').onClick = branch.deepviewCta; // Or generally + /// + /// // In HTML + /// + /// // We recommend to assign deepviewCta in deepview callback: + /// branch.deepview(data, option, function(err) { + /// if (err) { + /// throw err; + /// } + /// $('a.deepview-cta').click(branch.deepviewCta); + /// }); + /// + /// // You can call this function any time after branch.deepview() is finished by simply: + /// branch.deepviewCta(); + /// + /// When debugging, please call branch.deepviewCta() with an error callback like so: + /// + /// branch.deepviewCta(function(err) { + /// if (err) { + /// console.log(err); + /// } + /// }); + /// Referral System Rewarding Functionality + /// In a standard referral system, you have 2 parties: the original user and the invitee. Our system + /// is flexible enough to handle rewards for all users for any actions. Here are a couple example + /// scenarios: + /// + /// Reward the original user for taking action (eg. inviting, purchasing, etc) + /// Reward the invitee for installing the app from the original user's referral link + /// Reward the original user when the invitee takes action (eg. give the original user credit when + /// their the invitee buys something) + /// These reward definitions are created on the dashboard, under the 'Reward Rules' section in the + /// 'Referrals' tab on the dashboard. + /// + /// Warning: For a referral program, you should not use unique awards for custom events and redeem + /// pre-identify call. This can allow users to cheat the system. + @JS('deepviewCta') + external static void deepviewCta([Function errorCallback]); + + /// first(callback) + /// Parameters + /// + /// callback: function, optional - callback to read the + /// session data. + /// + /// Returns the same session information and any referring data, as + /// Branch.init did when the app was first installed. This is meant to be called + /// after Branch.init has been called if you need the first session information at a + /// later point. + /// If the Branch session has already been initialized, the callback will return + /// immediately, otherwise, it will return once Branch has been initialized. + @JS('first') + external static void first([Function callback]); + + // No documentation on reference + // @JS('getCode') + // external static void getCode(); + + /// init(branch_key, options, callback) + /// Parameters + /// + /// branch_key: string, required - Your Branch live key, or (deprecated) your app id. + /// + /// options: Object, optional - { }. + /// + /// callback: function, optional - callback to read the + /// session data. + /// + /// Adding the Branch script to your page automatically creates a window.branch + /// object with all the external methods described below. All calls made to + /// Branch methods are stored in a queue, so even if the SDK is not fully + /// instantiated, calls made to it will be queued in the order they were + /// originally called. + /// If the session was opened from a referring link, data() will also return the referring link + /// click as referring_link, which gives you the ability to continue the click flow. + /// + /// The init function on the Branch object initiates the Branch session and + /// creates a new user session, if it doesn't already exist, in + /// sessionStorage. + /// + /// Useful Tip: The init function returns a data object where you can read + /// the link the user was referred by. + /// + /// Properties available in the options object: + /// + /// Key Value + /// branch_match_id optional - string. The current user's browser-fingerprint-id. The value of this parameter should be the same as the value of ?branch_match_id (automatically appended by Branch after a link click). _Only necessary if ?_branch_match_id is lost due to multiple redirects in your flow. + /// branch_view_id optional - string. If you would like to test how Journeys render on your page before activating them, you can set the value of this parameter to the id of the view you are testing. Only necessary when testing a view related to a Journey. + /// no_journeys optional - boolean. When true, prevents Journeys from appearing on current page. + /// disable_entry_animation optional - boolean. When true, prevents a Journeys entry animation. + /// disable_exit_animation optional - boolean. When true, prevents a Journeys exit animation. + /// retries optional - integer. Value specifying the number of times that a Branch API call can be re-attempted. Default 2. + /// retry_delay optional - integer . Amount of time in milliseconds to wait before re-attempting a timed-out request to the Branch API. Default 200 ms. + /// timeout optional - integer. Duration in milliseconds that the system should wait for a response before considering any Branch API call to have timed out. Default 5000 ms. + /// metadata optional - object. Key-value pairs used to target Journeys users via the "is viewing a page with metadata key" filter. + /// nonce optional - string. A nonce value that will be added to branch-journey-cta injected script. Used to allow that script from a Content Security Policy. + /// tracking_disabled optional - boolean. true disables tracking + /// Usage + /// + /// branch.init( + /// branch_key, + /// options, + /// callback (err, data), + /// ); + /// Callback Format + /// + /// callback( + /// "Error message", + /// { + /// data_parsed: { }, // If the user was referred from a link, and the link has associated data, the data is passed in here. + /// referring_identity: '12345', // If the user was referred from a link, and the link was created by a user with an identity, that identity is here. + /// has_app: true, // Does the user have the app installed already? + /// identity: 'BranchUser', // Unique string that identifies the user + /// ~referring_link: 'https://bnc.lt/c/jgg75-Gjd3' // The referring link click, if available. + /// } + /// ); + /// Note: Branch.init must be called prior to calling any other Branch functions. + @JS('init') + external static void init(String branchKey, + [Object? options, Function? callback]); + + /// link(data, callback) + /// Parameters + /// + /// data: Object, required - link data and metadata. + /// + /// callback: function, required - returns a string of the Branch deep + /// linking URL. + /// + /// Formerly createLink() + /// + /// Creates and returns a deep linking URL. The data parameter can include an + /// object with optional data you would like to store, including Facebook + /// Open Graph data. + /// + /// data The dictionary to embed with the link. Accessed as session or install parameters from + /// the SDK. + /// + /// Note + /// You can customize the Facebook OG tags of each URL if you want to dynamically share content by + /// using the following optional keys in the data dictionary. Please use this + /// Facebook tool to debug your OG tags! + /// + /// Key Value + /// "$og_title" The title you'd like to appear for the link in social media + /// "$og_description" The description you'd like to appear for the link in social media + /// "$og_image_url" The URL for the image you'd like to appear for the link in social media + /// "$og_video" The URL for the video + /// "$og_url" The URL you'd like to appear + /// "$og_redirect" If you want to bypass our OG tags and use your own, use this key with the URL that contains your site's metadata. + /// Also, you can set custom redirection by inserting the following optional keys in the dictionary: + /// + /// Key Value + /// "$desktop_url" Where to send the user on a desktop or laptop. By default it is the Branch-hosted text-me service + /// "$android_url" The replacement URL for the Play Store to send the user if they don't have the app. Only necessary if you want a mobile web splash + /// "$ios_url" The replacement URL for the App Store to send the user if they don't have the app. Only necessary if you want a mobile web splash + /// "$ipad_url" Same as above but for iPad Store + /// "$fire_url" Same as above but for Amazon Fire Store + /// "$blackberry_url" Same as above but for Blackberry Store + /// "$windows_phone_url" Same as above but for Windows Store + /// "$after_click_url" When a user returns to the browser after going to the app, take them to this URL. iOS only; Android coming soon + /// You have the ability to control the direct deep linking of each link as well: + /// + /// Key Value + /// "$deeplink_path" The value of the deep link path that you'd like us to append to your URI. For example, you could specify "$deeplink_path": "radio/station/456" and we'll open the app with the URI "yourapp://radio/station/456?link_click_id=branch-identifier". This is primarily for supporting legacy deep linking infrastructure. + /// "$always_deeplink" true or false. (default is not to deep link first) This key can be specified to have our linking service force try to open the app, even if we're not sure the user has the app installed. If the app is not installed, we fall back to the respective app store or $platform_url key. By default, we only open the app if we've seen a user initiate a session in your app from a Branch link (has been cookied and deep linked by Branch). + /// Usage + /// + /// branch.link( + /// data, + /// callback (err, link) + /// ); + /// Example + /// + /// branch.link({ + /// tags: [ 'tag1', 'tag2' ], + /// channel: 'facebook', + /// feature: 'dashboard', + /// stage: 'new user', + /// data: { + /// mydata: 'something', + /// foo: 'bar', + /// '$desktop_url': 'http://myappwebsite.com', + /// '$ios_url': 'http://myappwebsite.com/ios', + /// '$ipad_url': 'http://myappwebsite.com/ipad', + /// '$android_url': 'http://myappwebsite.com/android', + /// '$og_app_id': '12345', + /// '$og_title': 'My App', + /// '$og_description': 'My app\'s description.', + /// '$og_image_url': 'http://myappwebsite.com/image.png' + /// } + /// }, function(err, link) { + /// console.log(err, link); + /// }); + /// Callback Format + /// + /// callback( + /// "Error message", + /// 'https://bnc.lt/l/3HZMytU-BW' // Branch deep linking URL + /// ); + @JS('link') + external static void link(Object data, Function callback); + + /// logout(callback) + /// Parameters + /// + /// callback: function, optional + /// + /// Logs out the current session, replaces session IDs and identity IDs. + /// + /// Usage + /// + /// branch.logout( + /// callback (err) + /// ); + /// Callback Format + /// + /// callback( + /// "Error message" + /// ); + @JS('logout') + external static void logout([Function callback]); + + /// removeListener(listener) + /// Parameters + /// + /// listener: function, required - Reference to the listening function you + /// would like to remove. note: this must be the same reference that was passed to + /// branch.addListener(), not an identical clone of the function. + /// + /// Remove the listener from observations, if it is present. Not that this function must be + /// passed a referrence to the same function that was passed to branch.addListener(), not + /// just an identical clone of the function. + @JS('removeListener') + external static void removeListener(Function listener); + + /// sendSMS(phone, linkData, options, callback) + /// Parameters + /// + /// phone: string, required - phone number to send SMS to + /// + /// linkData: Object, required - object of link data + /// + /// options: Object, optional - options: make_new_link, which forces the creation of a + /// new link even if one already exists + /// + /// callback: function, optional - Returns an error if unsuccessful + /// + /// Formerly SMSLink() + /// + /// A robust function to give your users the ability to share links via SMS. If + /// the user navigated to this page via a Branch link, sendSMS will send that + /// same link. Otherwise, it will create a new link with the data provided in + /// the params argument. sendSMS also registers a click event with the + /// channel pre-filled with 'sms' before sending an sms to the provided + /// phone parameter. This way the entire link click event is recorded starting + /// with the user sending an sms. + /// + /// Note: sendSMS will automatically send a previously generated link click, + /// along with the data object in the original link. Therefore, it is unneccessary for the + /// data() method to be called to check for an already existing link. If a link already + /// exists, sendSMS will simply ignore the data object passed to it, and send the existing link. + /// If this behavior is not desired, set make_new_link: true in the options object argument + /// of sendSMS, and sendSMS will always make a new link. + /// + /// Supports international SMS. + /// + /// Please note that the destination phone number needs to be from the same country the SMS is being sent from. + /// + /// Usage + /// + /// branch.sendSMS( + /// phone, + /// linkData, + /// options, + /// callback (err, data) + /// ); + /// Example + /// + /// var linkData = { + /// tags: ['tag1', 'tag2'], + /// channel: 'Website', + /// feature: 'TextMeTheApp', + /// data: { + /// // here's how to define the deeplink_path and the custom text on the front end + /// $deeplink_path: `custom_deeplink_path`, + /// //for the custom text, use {{link}} as a macro for link location + /// $custom_sms_text: `Here's my custom text, and here is the {{ link }}`, + /// mydata: 'something', + /// foo: 'bar', + /// '$desktop_url': 'http://myappwebsite.com', + /// '$ios_url': 'http://myappwebsite.com/ios', + /// '$ipad_url': 'http://myappwebsite.com/ipad', + /// '$android_url': 'http://myappwebsite.com/android', + /// '$og_app_id': '12345', + /// '$og_title': 'My App', + /// '$og_description': 'My app\'s description.', + /// '$og_image_url': 'http://myappwebsite.com/image.png' + /// } + /// } + /// branch.sendSMS( + /// '9999999999', + /// linkData, + /// { make_new_link: false }, // Default: false. If set to true, sendSMS will generate a new link even if one already exists. + /// function(err) { console.log(err); } + /// ); + /// Callback Format + /// + /// callback("Error message"); + @JS('sendSMS') + external static void sendSMS(String phone, Object linkData, + [Object options, Function callback]); + + /// setBranchViewData(data) + /// Parameters + /// + /// data: Object, required - object of all link data, same as Branch.link() + /// + /// This function lets you set the deep link data dynamically for a given mobile web Journey. For + /// example, if you desgin a full page interstitial, and want the deep link data to be custom for each + /// page, you'd need to use this function to dynamically set the deep link params on page load. Then, + /// any Journey loaded on that page will inherit these deep link params. + /// + /// Usage + /// + /// branch.setBranchViewData( + /// data // Data for link, same as Branch.link() + /// ); + /// Example + /// + /// branch.setBranchViewData({ + /// tags: ['tag1', 'tag2'], + /// data: { + /// mydata: 'something', + /// foo: 'bar', + /// '$deeplink_path': 'open/item/1234' + /// } + /// }); + @JS('setBranchViewData') + external static void setBranchViewData(Object data); + + /// setIdentity(identity, callback) + /// Parameters + /// + /// identity: string, required - a string uniquely identifying the user - often a user ID + /// or email address. + /// + /// callback: function, optional - callback that returns the user's + /// Branch identity id and unique link. + /// + /// Formerly identify() + /// + /// Sets the identity of a user and returns the data. To use this function, pass + /// a unique string that identifies the user - this could be an email address, + /// UUID, Facebook ID, etc. + /// + /// Usage + /// + /// branch.setIdentity( + /// identity, + /// callback (err, data) + /// ); + /// Callback Format + /// + /// callback( + /// "Error message", + /// { + /// identity_id: '12345', // Server-generated ID of the user identity, stored in `sessionStorage`. + /// link: 'url', // New link to use (replaces old stored link), stored in `sessionStorage`. + /// referring_data_parsed: { }, // Returns the initial referring data for this identity, if exists, as a parsed object. + /// referring_identity: '12345' // Returns the initial referring identity for this identity, if exists. + /// } + /// ); + @JS('setIdentity') + external static void setIdentity(String identity, [Function callback]); + + /// track(event, metadata, callback) + /// Parameters + /// + /// event: string, required - name of the event to be tracked. + /// + /// metadata: Object, optional - object of event metadata. + /// + /// callback: function, optional + /// + /// This function allows you to track any event with supporting metadata. + /// The metadata parameter is a formatted JSON object that can contain + /// any data and has limitless hierarchy + /// + /// Usage + /// + /// branch.track( + /// event, + /// metadata, + /// callback (err) + /// ); + /// Callback Format + /// + /// callback("Error message"); + @JS('track') + external static void track(String event, + [Object metadata, Function callback]); + + /// trackCommerceEvent(event, commerce_data, metadata, callback) + /// Parameters + /// + /// event: String, required - Name of the commerce event to be tracked. We currently support 'purchase' events + /// + /// commerce_data: Object, required - Data that describes the commerce event + /// + /// metadata: Object, optional - metadata you may want add to the event + /// + /// callback: function, optional - Returns an error if unsuccessful + /// + /// Sends a user commerce event to the server + /// + /// Use commerce events to track when a user purchases an item in your online store, + /// makes an in-app purchase, or buys a subscription. The commerce events are tracked in + /// the Branch dashboard along with your other events so you can judge the effectiveness of + /// campaigns and other analytics. + /// + /// Usage + /// + /// branch.trackCommerceEvent( + /// event, + /// commerce_data, + /// metadata, + /// callback (err) + /// ); + /// Example + /// + /// var commerce_data = { + /// "revenue": 50.0, + /// "currency": "USD", + /// "transaction_id": "foo-transaction-id", + /// "shipping": 0.0, + /// "tax": 5.0, + /// "affiliation": "foo-affiliation", + /// "products": [ + /// { "sku": "foo-sku-1", "name": "foo-item-1", "price": 45.00, "quantity": 1, "brand": "foo-brand", + /// "category": "Electronics", "variant": "foo-variant-1"}, + /// { "sku": "foo-sku-2", "price": 2.50, "quantity": 2} + /// ], + /// }; + /// + /// var metadata = { "foo": "bar" }; + /// + /// branch.trackCommerceEvent('purchase', commerce_data, metadata, function(err) { + /// if(err) { + /// throw err; + /// } + /// }); + @JS('trackCommerceEvent') + external static void trackCommerceEvent(String name, Object commerceData, + [Object metadata, Function callback]); + + /// logEvent(event, event_data_and_custom_data, content_items, customer_event_alias, callback) + /// Parameters + /// + /// event: String, required + /// + /// event_data_and_custom_data: Object, optional + /// + /// content_items: Array, optional + /// + /// customer_event_alias: String, optional + /// + /// callback: function, optional + /// + /// Register commerce events, content events, user lifecycle events and custom events via logEvent() + /// + /// NOTE: If this is the first time you are integrating our new event tracking feature via logEvent(), please use the latest Branch WebSDK snippet from the Installation section. This has been updated in v2.30.0 of our SDK. + /// + /// The guides below provide information about what keys can be sent when triggering these event types: + /// + /// Logging Commerce Events + /// Logging Content Events + /// Logging User Lifecycle + /// Logging Custom Events + /// Usage for Commerce, Content & User Lifecycle "Standard Events" + /// + /// branch.logEvent( + /// event, + /// event_data_and_custom_data, + /// content_items, + /// customer_event_alias, + /// callback (err) + /// ); + /// Usage for "Custom Events" + /// + /// JavaScript + /// JavaScript + /// branch.logEvent( + /// event, + /// custom_data, + /// callback (err) + /// ); + /// `` + /// **Notes**: + /// - logEvent() sends user_data automatically + /// - When firing Standard Events, send custom and event data as part of the same object + /// - Custom Events do not contain content items and event data + /// + /// ### Example -- How to log a Commerce Event + /// var event_and_custom_data = { + /// "transaction_id": "tras_Id_1232343434", + /// "currency": "USD", + /// "revenue": 180.2, + /// "shipping": 10.5, + /// "tax": 13.5, + /// "coupon": "promo-1234", + /// "affiliation": "high_fi", + /// "description": "Preferred purchase", + /// "purchase_loc": "Palo Alto", + /// "store_pickup": "unavailable" + /// }; + /// var content_items = [ + /// { + /// "$content_schema": "COMMERCE_PRODUCT", + /// "$og_title": "Nike Shoe", + /// "$og_description": "Start loving your steps", + /// "$og_image_url": "http:///example.com/img1.jpg", + /// "$canonical_identifier": "nike/1234", + /// "$publicly_indexable": false, + /// "$price": 101.2, + /// "$locally_indexable": true, + /// "$quantity": 1, + /// "$sku": "1101123445", + /// "$product_name": "Runner", + /// "$product_brand": "Nike", + /// "$product_category": "Sporting Goods", + /// "$product_variant": "XL", + /// "$rating_average": 4.2, + /// "$rating_count": 5, + /// "$rating_max": 2.2, + /// "$creation_timestamp": 1499892854966, + /// "$exp_date": 1499892854966, + /// "$keywords": [ "sneakers", "shoes" ], + /// "$address_street": "230 South LaSalle Street", + /// "$address_city": "Chicago", + /// "$address_region": "IL", + /// "$address_country": "US", + /// "$address_postal_code": "60604", + /// "$latitude": 12.07, + /// "$longitude": -97.5, + /// "$image_captions": [ "my_img_caption1", "my_img_caption_2" ], + /// "$condition": "NEW", + /// "$custom_fields": {"foo1":"bar1","foo2":"bar2"} + /// }, + /// { + /// "$og_title": "Nike Woolen Sox", + /// "$canonical_identifier": "nike/5324", + /// "$og_description": "Fine combed woolen sox for those who love your foot", + /// "$publicly_indexable": false, + /// "$price": 80.2, + /// "$locally_indexable": true, + /// "$quantity": 5, + /// "$sku": "110112467", + /// "$product_name": "Woolen Sox", + /// "$product_brand": "Nike", + /// "$product_category": "Apparel & Accessories", + /// "$product_variant": "Xl", + /// "$rating_average": 3.3, + /// "$rating_count": 5, + /// "$rating_max": 2.8, + /// "$creation_timestamp": 1499892854966 + /// }]; + /// var customer_event_alias = "event alias"; + /// branch.logEvent( + /// "PURCHASE", + /// event_and_custom_data, + /// content_items, + /// customer_event_alias, + /// function(err) { console.log(err); } + /// ); + + @JS('logEvent') + external static void logEvent(String event, + [Object eventDataAndCustomData, + JsArray contentItems, + String customerEventAlias, + Function callback]); + + /// disableTracking(disableTracking) + /// Parameters + /// + /// disableTracking: Boolean, optional - true disables tracking and false re-enables tracking. + /// + /// Notes: + /// + /// disableTracking() without a parameter is a shorthand for disableTracking(true). + /// If a call to disableTracking(false) is made, the WebSDK will re-initialize. Additionally, if tracking_disabled: true is passed + /// as an option to init(), it will be removed during the reinitialization process. + /// Allows User to Remain Private + /// + /// This will prevent any Branch requests from being sent across the network, except for the case of deep linking. + /// If someone clicks a Branch link, but has expressed not to be tracked, we will return deep linking data back to the + /// client but without tracking information. + /// + /// In do-not-track mode, you will still be able to create links and display Journeys however, they will not have identifiable + /// information associated to them. You can change this behavior at any time, by calling the aforementioned function. + /// The do-not-track mode state is persistent: it is saved for the user across browser sessions for the web site. + @JS('disableTracking') + external static void disableTracking([bool disableTracking]); + + /// lastAttributedTouchData (number, callback) + /// + /// number -attribution_window - the number of days to look up attribution data + /// callback: function, optional - returns last attributed touch data + /// + /// Returns last attributed touch data for current user. Last attributed touch + /// data has the information associated with that user's last viewed impression + /// or clicked link. + /// Usage + /// + /// branch.lastAttributedTouchData( + /// attribution_window, + /// callback (err, data) + /// ); + /// Example + /// + /// branch.lastAttributedTouchData( + /// 10, + /// function(err, data) { + /// console.log(err, data); + /// }); + /// Callback Format + /// + /// callback( + /// "Error message", + /// '{}' + /// ); + @JS('lastAttributedTouchData') + external static void lastAttributedTouchData(attributionWindow, + [Function callback]); + + /// qrcode(data, callback) + /// Parameters + /// + /// data: Object, required - link data and metadata. + ///qrCodeSettings: Object required -QR code Settings + /// + /// callback: function, required - returns a string of the Branch deep + /// linking URL. + /// + /// Formerly createLink() + /// + /// Creates and returns a deep linking URL. The data parameter can include an + /// object with optional data you would like to store, including Facebook + /// Open Graph data. + /// + /// data The dictionary to embed with the link. Accessed as session or install parameters from + /// the SDK. + /// + /// Note + /// You can customize the Facebook OG tags of each URL if you want to dynamically share content by + /// using the following optional keys in the data dictionary. Please use this + /// Facebook tool to debug your OG tags! + /// + /// Key Value + /// "$og_title" The title you'd like to appear for the link in social media + /// "$og_description" The description you'd like to appear for the link in social media + /// "$og_image_url" The URL for the image you'd like to appear for the link in social media + /// "$og_video" The URL for the video + /// "$og_url" The URL you'd like to appear + /// "$og_redirect" If you want to bypass our OG tags and use your own, use this key with the URL that contains your site's metadata. + /// Also, you can set custom redirection by inserting the following optional keys in the dictionary: + /// + /// Key Value + /// "$desktop_url" Where to send the user on a desktop or laptop. By default it is the Branch-hosted text-me service + /// "$android_url" The replacement URL for the Play Store to send the user if they don't have the app. Only necessary if you want a mobile web splash + /// "$ios_url" The replacement URL for the App Store to send the user if they don't have the app. Only necessary if you want a mobile web splash + /// "$ipad_url" Same as above but for iPad Store + /// "$fire_url" Same as above but for Amazon Fire Store + /// "$blackberry_url" Same as above but for Blackberry Store + /// "$windows_phone_url" Same as above but for Windows Store + /// "$after_click_url" When a user returns to the browser after going to the app, take them to this URL. iOS only; Android coming soon + /// You have the ability to control the direct deep linking of each link as well: + /// + /// Key Value + /// "$deeplink_path" The value of the deep link path that you'd like us to append to your URI. For example, you could specify "$deeplink_path": "radio/station/456" and we'll open the app with the URI "yourapp://radio/station/456?link_click_id=branch-identifier". This is primarily for supporting legacy deep linking infrastructure. + /// "$always_deeplink" true or false. (default is not to deep link first) This key can be specified to have our linking service force try to open the app, even if we're not sure the user has the app installed. If the app is not installed, we fall back to the respective app store or $platform_url key. By default, we only open the app if we've seen a user initiate a session in your app from a Branch link (has been cookied and deep linked by Branch). + /// Usage + /// + /// branch.link( + /// data, + /// callback (err, link) + /// ); + /// Example + /// + /// branch.link({ + /// tags: [ 'tag1', 'tag2' ], + /// channel: 'facebook', + /// feature: 'dashboard', + /// stage: 'new user', + /// data: { + /// mydata: 'something', + /// foo: 'bar', + /// '$desktop_url': 'http://myappwebsite.com', + /// '$ios_url': 'http://myappwebsite.com/ios', + /// '$ipad_url': 'http://myappwebsite.com/ipad', + /// '$android_url': 'http://myappwebsite.com/android', + /// '$og_app_id': '12345', + /// '$og_title': 'My App', + /// '$og_description': 'My app\'s description.', + /// '$og_image_url': 'http://myappwebsite.com/image.png' + /// } + /// }, function(err, link) { + /// console.log(err, link); + /// }); + /// Callback Format + /// + /// callback( + /// "Error message", + /// 'https://bnc.lt/l/3HZMytU-BW' // Branch deep linking URL + /// ); + + @JS('qrCode') + external static void qrCode(Object qrCodeLinkData, Object qrCodeSettings, + Function(String? err, QrCodeData? qrCode) callback); +} diff --git a/pubspec.lock b/pubspec.lock index 0fd4fb87..2feb16f3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -56,12 +56,12 @@ packages: source: sdk version: "0.0.0" flutter_lints: - dependency: "direct main" + dependency: "direct dev" description: name: flutter_lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.4" + version: "2.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -85,7 +85,7 @@ packages: name: lints url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "2.0.0" matcher: dependency: transitive description: @@ -120,7 +120,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -176,5 +176,5 @@ packages: source: hosted version: "2.1.2" sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.17.3 <3.0.0" + flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index e9879092..3e827ef2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,32 +1,47 @@ -name: flutter_branch_sdk -description: Flutter Plugin for create deep link using Brach SDK (https://branch.io). This plugin provides a cross-platform (iOS, Android). -version: 5.1.1 -homepage: https://github.com/RodrigoSMarques/flutter_branch_sdk - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.22.0" - -flutter: - plugin: - platforms: - android: - package: br.com.rsmarques.flutter_branch_sdk - pluginClass: FlutterBranchSdkPlugin - ios: - pluginClass: FlutterBranchSdkPlugin - web: - fileName: src/flutter_branch_sdk_web.dart - pluginClass: FlutterBranchSdk - -dependencies: - flutter: - sdk: flutter - flutter_web_plugins: - sdk: flutter - plugin_platform_interface: ">=1.0.0 <3.0.0" - js: ^0.6.4 - -dev_dependencies: - flutter_test: - sdk: flutter +name: flutter_branch_sdk +description: Flutter Plugin for create deep link using Brach SDK (https://branch.io). This plugin provides a cross-platform (iOS, Android, Web). +version: 6.0.0 +homepage: https://github.com/RodrigoSMarques/flutter_branch_sdk + +environment: + sdk: ">=2.17.3 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + flutter_web_plugins: + sdk: flutter + plugin_platform_interface: ^2.1.2 + js: ^0.6.4 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: br.com.rsmarques.flutter_branch_sdk + pluginClass: FlutterBranchSdkPlugin + ios: + pluginClass: FlutterBranchSdkPlugin + web: + pluginClass: FlutterBranchSdkWeb + fileName: src/flutter_branch_sdk_web.dart diff --git a/tool/build-apk.sh b/tool/build-apk.sh new file mode 100644 index 00000000..c973a025 --- /dev/null +++ b/tool/build-apk.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# This scrip installs the dependencies of the flutter package. +# parse_server_sdk is set to the relative path. + +cd example +flutter config --no-analytics +flutter pub get +flutter build apk \ No newline at end of file diff --git a/tool/build-web.sh b/tool/build-web.sh new file mode 100644 index 00000000..bd706302 --- /dev/null +++ b/tool/build-web.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# This scrip installs the dependencies of the flutter package. +# parse_server_sdk is set to the relative path. + +cd example +flutter config --no-analytics +flutter pub get +#flutter build web --source-maps +#flutter build web --profile --source-maps --dart-define=Dart2jsOptimization=O0 +flutter build web \ No newline at end of file diff --git a/tool/flutter-dependencies.sh b/tool/flutter-dependencies.sh new file mode 100644 index 00000000..4680e543 --- /dev/null +++ b/tool/flutter-dependencies.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# This scrip installs the dependencies of the flutter package. +# parse_server_sdk is set to the relative path. +flutter pub get diff --git a/translation/pt-BR/README.md b/translation/pt-BR/README.md deleted file mode 100644 index e45e14a7..00000000 --- a/translation/pt-BR/README.md +++ /dev/null @@ -1,406 +0,0 @@ -Language: [English](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/README.md) | [Português](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/translation/pt-BR/README.md) - -# flutter_branch_sdk - -Flutter plugin que implementa Branch SDK (https://branch.io). - -Branch.io ajuda os aplicativos móveis a crescer com deep links que aumentam os sistemas de referência, compartilhando links e convites com atribuição e análise completa. - -Suporta Android e iOS. -* Android - Branch SDK Version >= 5.0.1 [Histório da versão Android](https://help.branch.io/developers-hub/docs/android-version-history) -* iOS - Branch SDK Version >= 0.33.0 [Histórico da versão iOS](https://help.branch.io/developers-hub/docs/ios-version-history) - -Funções implementadas neste plugin: - -* Teste da Integração Branch -* Rastreamento de usuário -* Habilitar / Desativar rastreamento de usuários -* Obter o primeiro e o último parâmetros -* Gerar Deep Link para Branch Universal Object (BUO) -* Exibir página de compartilhamento para Branch Universal Object (BUO) -* Listar / Remover BUO na pesquisa do Android e iOS -* Registrar visualizações -* Rastrear ações e eventos do usuário -* Recompensas por indicação - -## Começando -### Configurar o Painel da Branch -* Registrar seu aplicativo -* Completar a integração básica no [Branch Dashboard](https://dashboard.branch.io/login) - -Para detalhes visutar: -* [iOS - somente a seção: **Configure Branch**](https://help.branch.io/developers-hub/docs/ios-basic-integration#configure-branch) -* [Android - somente a seção: **Configure Branch Dashboard**](https://help.branch.io/developers-hub/docs/android-basic-integration#configure-branch-dashboard) - -## Configurar os projetos em cada Plataforam -### Integração Android - -Siga as etapas na página [https://help.branch.io/developers-hub/docs/android-basic-integration#configure-app](https://help.branch.io/developers-hub/docs/android-basic-integration#configure-app), session _**Configure app**_: -* Add Branch to your `AndroidManifest.xml` - -### Integração iOS -Siga as etapas na página [https://help.branch.io/developers-hub/docs/ios-basic-integration#configure-bundle-identifier](https://help.branch.io/developers-hub/docs/ios-basic-integration#configure-bundle-identifier), from session ```Configure bundle identifier```: -* Configure bundle identifier -* Configure associated domains -* Configure entitlements -* Configure Info.plist -* Confirm app prefix - -Nota 1: Branch SDK 0.32.0 requer `iOS 9.0`.
- Atualize a versão mínima no projeto, na seção **"Deployment Info" -> "Target"**. - -Nota 2: No `Info.plist` não adicionar `branch_key` `live` e `test` ao mesmo tempo.
-Use somente `branch_key` e atualize se necessário. - - -## Instalação -Para usar este plugin, adicione `flutter_branch_sdk` na seção [dependency do seu arquivo pubspec.yaml](https://flutter.io/platform-plugins/). - -## Como usuar - -### Testar a integração Branch -Teste se a configuração do Branch foi realizada no código nativo chamando: -```dart -FlutterBranchSdk.validateSDKIntegration(); -``` -Verifique os logs para garantir que todos os testes de integração do SDK foram aprovados. - -Exemplo da log: -```java -------------------- Initiating Branch integration verification --------------------------- ... -1. Verifying Branch instance creation ... -Passed -2. Checking Branch keys ... -Passed -3. Verifying application package name ... -Passed -4. Checking Android Manifest for URI based deep link config ... -Passed -5. Verifying URI based deep link config with Branch dash board. ... -Passed -6. Verifying intent for receiving URI scheme. ... -Passed -7. Checking AndroidManifest for AppLink config. ... -Passed -8. Verifying any supported custom link domains. ... -Passed -9. Verifying default link domains integrations. ... -Passed -10. Verifying alternate link domains integrations. ... -Passed -Passed --------------------------------------------- -Successfully completed Branch integration validation. Everything looks good! - -Great! Comment out the 'validateSDKIntegration' line in your app. Next check your deep link routing. -Append '?bnc_validate=true' to any of your app's Branch links and click it on your mobile device (not the Simulator!) to start the test. -For instance, to validate a link like: -https://.app.link/NdJ6nFzRbK -click on: -https://.app.link/NdJ6nFzRbK?bnc_validate=true -``` -Certifique-se de comentar ou remover `validateSDKIntegration` na versão de release para produção. - -### Inicializar Branch ler o deep link -```dart - StreamSubscription streamSubscription = FlutterBranchSdk.initSession().listen((data) { - if (data.containsKey("+clicked_branch_link") && - data["+clicked_branch_link"] == true) { - //Link clicked. Add logic to get link data - print('Custom string: ${data["custom_string"]}'); - } - }, onError: (error) { - PlatformException platformException = error as PlatformException; - print( - 'InitSession error: ${platformException.code} - ${platformException.message}'); - }); -``` -### Recuperar parâmetros de instalação (somente instalação) -These session parameters will be available at any point later on with this command. If no parameters are available then Branch will return an empty dictionary. This refreshes with every new session (app installs AND app opens). -Esses parâmetros da sessão estarão disponíveis a qualquer momento posteriormente com este comando. Se nenhum parâmetro estiver disponível, o Branch retornará um dicionário vazio. Isso é atualizado a cada nova sessão (na instalação do aplicativo e o aplicativo é aberto). -```dart - Map params = await FlutterBranchSdk.getFirstReferringParams(); -``` -### Retrieve session (install or open) parameters -If you ever want to access the original session params (the parameters passed in for the first install event only), you can use this line. This is useful if you only want to reward users who newly installed the app from a referral link -```dart - Map params = await FlutterBranchSdk.getLatestReferringParams(); -``` -### Create content reference -The Branch Universal Object encapsulates the thing you want to share. -```dart - BranchUniversalObject buo = BranchUniversalObject( - canonicalIdentifier: 'flutter/branch', - title: 'Flutter Branch Plugin', - imageUrl: 'https://flutter.dev/assets/flutter-lockup-4cb0ee072ab312e59784d9fbf4fb7ad42688a7fdaea1270ccf6bbf4f34b7e03f.svg', - contentDescription: 'Flutter Branch Description', - keywords: ['Plugin', 'Branch', 'Flutter'], - publiclyIndex: true, - locallyIndex: true, - contentMetadata: BranchContentMetaData()..addCustomMetadata('custom_string', 'abc') - ..addCustomMetadata('custom_number', 12345) - ..addCustomMetadata('custom_bool', true) - ..addCustomMetadata('custom_list_number', [1,2,3,4,5 ]) - ..addCustomMetadata('custom_list_string', ['a', 'b', 'c']), - ); -``` -### Create link reference -* Generates the analytical properties for the deep link -* Used for Create deep link and Share deep link -```dart - BranchLinkProperties lp = BranchLinkProperties( - channel: 'facebook', - feature: 'sharing', - stage: 'new share', - tags: ['one', 'two', 'three'] - ); - lp.addControlParam('url', 'http://www.google.com'); - lp.addControlParam('url2', 'http://flutter.dev'); -``` -### Create deep link -Generates a deep link within your app -```dart - BranchResponse response = - await FlutterBranchSdk.getShortUrl(buo: buo, linkProperties: lp); - if (response.success) { - print('Link generated: ${response.result}'); - } else { - print('Error : ${response.errorCode} - ${response.errorMessage}'); - } -``` -### Show Share Sheet deep link -Will generate a Branch deep link and tag it with the channel the user selects. -Note: _For Android additional customization is possible_ -```dart - BranchResponse response = await FlutterBranchSdk.showShareSheet( - buo: buo, - linkProperties: lp, - messageText: 'My Share text', - androidMessageTitle: 'My Message Title', - androidSharingTitle: 'My Share with'); - - if (response.success) { - print('showShareSheet Sucess'); - } else { - print('Error : ${response.errorCode} - ${response.errorMessage}'); - } -``` -### List content on Search -* For Android list BUO links in Google Search with App Indexing -* For iOs list BUO links in Spotlight - -Enable automatic sitemap generation on the Organic Search page of the [Branch Dashboard](https://dashboard.branch.io/search). -Check the Automatic sitemap generation checkbox. - -```dart - bool success = await FlutterBranchSdk.listOnSearch(buo: buo); - print(success); -``` -### Remove content from Search -Privately indexed Branch Universal Object can be removed -```dart - bool success = await FlutterBranchSdk.removeFromSearch(buo: buo); - print('Remove sucess: $success'); -``` -### Register Event VIEW_ITEM -Mark the content referred by this object as viewed. This increment the view count of the contents referred by this object. -```dart -FlutterBranchSdk.registerView(buo: buo); -``` - -### Tracking User Actions and Events -Use the `BranchEvent` interface to track special user actions or application specific events beyond app installs, opens, and sharing. You can track events such as when a user adds an item to an on-line shopping cart, or searches for a keyword, among others. -The `BranchEvent` interface provides an interface to add contents represented by `BranchUniversalObject` in order to associate app contents with events. -Analytics about your app's BranchEvents can be found on the Branch dashboard, and BranchEvents also provide tight integration with many third party analytics providers. -```dart -BranchEvent eventStandart = BranchEvent.standardEvent(BranchStandardEvent.ADD_TO_CART); -FlutterBranchSdk.trackContent(buo: buo, branchEvent: eventStandart); -``` -You can use your own custom event names too: -```dart -BranchEvent eventCustom = BranchEvent.customEvent('Custom_event'); -FlutterBranchSdk.trackContent(buo: buo, branchEvent: eventCustom); -``` -Extra event specific data can be tracked with the event as well: -```dart - eventStandart.transactionID = '12344555'; - eventStandart.currency = BranchCurrencyType.BRL; - eventStandart.revenue = 1.5; - eventStandart.shipping = 10.2; - eventStandart.tax = 12.3; - eventStandart.coupon = 'test_coupon'; - eventStandart.affiliation = 'test_affiliation'; - eventStandart.eventDescription = 'Event_description'; - eventStandart.searchQuery = 'item 123'; - eventStandart.adType = BranchEventAdType.BANNER; - eventStandart.addCustomData( - 'Custom_Event_Property_Key1', 'Custom_Event_Property_val1'); - eventStandart.addCustomData( - 'Custom_Event_Property_Key2', 'Custom_Event_Property_val2'); - FlutterBranchSdk.trackContent(buo: buo, branchEvent: eventStandart); -``` - -You can register logs in BranchEvent without Branch Universal Object (BUO) for tracking and analytics: -```dart -BranchEvent eventStandart = BranchEvent.standardEvent(BranchStandardEvent.ADD_TO_CART); -FlutterBranchSdk.trackContentWithoutBuo(branchEvent: eventStandart); -``` -You can use your own custom event names too: -```dart -BranchEvent eventCustom = BranchEvent.customEvent('Custom_event'); -FlutterBranchSdk.trackContentWithoutBuo(branchEvent: eventCustom); -``` - -### Track users -Sets the identity of a user (email, ID, UUID, etc) for events, deep links, and referrals -```dart -//login -FlutterBranchSdk.setIdentity('user1234567890'); -``` -```dart -//logout -FlutterBranchSdk.logout(); -``` -```dart -//check if user is identify - bool isUserIdentified = await FlutterBranchSdk.isUserIdentified(); -``` - - -### Enable or Disable User Tracking -If you need to comply with a user's request to not be tracked for GDPR purposes, or otherwise determine that a user should not be tracked, utilize this field to prevent Branch from sending network requests. This setting can also be enabled across all users for a particular link, or across your Branch links. -```dart -FlutterBranchSdk.disableTracking(false); -``` -```dart -FlutterBranchSdk.disableTracking(true); -``` -You can choose to call this throughout the lifecycle of the app. Once called, network requests will not be sent from the SDKs. Link generation will continue to work, but will not contain identifying information about the user. In addition, deep linking will continue to work, but will not track analytics for the user. - -### Referral System Rewarding Functionality -Reward balances change randomly on the backend when certain actions are taken (defined by your rules), so you'll need to make an asynchronous call to retrieve the balance. -Read more here: https://docs.branch.io/viral/referrals/#search - - -#### Get Reward Balance -Reward balances change randomly on the backend when certain actions are taken (defined by your rules), so you'll need to make call to retrieve the balance. Here is the syntax: - -***optional parameter***: bucket - value containing the name of the referral bucket to attempt to redeem credits from - -```dart -BranchResponse response = - await FlutterBranchSdk.loadRewards(); -if (response.success) { - credits = response.result; - print('Crédits'); -} else { - print('Credits error: ${response.errorMessage}'); -} -``` - - -#### Redeem All or Some of the Reward Balance (Store State) -Redeeming credits allows users to cash in the credits they've earned. Upon successful redemption, the user's balance will be updated reflecting the deduction. - -***optional parameter***: bucket - value containing the name of the referral bucket to attempt to redeem credits from - -```dart -BranchResponse response = - await FlutterBranchSdk.redeemRewards(count: 5); -if (response.success) { - print('Redeeming Credits with success'); -} else { - print('Redeeming Credits with error: ${response.errorMessage}'); -} -``` -#### Get Credit History -This call will retrieve the entire history of credits and redemptions from the individual user. To use this call, implement like so: - -***optional parameter***: bucket - value containing the name of the referral bucket to attempt to redeem credits from - -```dart -BranchResponse response = - await FlutterBranchSdk.getCreditHistory(); -if (response.success) { - print('getCreditHistory with success. Records: ${(response.result as List).length}'); -} else { - print('getCreditHistory with error: ${response.errorMessage}'); -} -``` -The response will return an list of map: -```json -[ - { - "transaction": { - "date": "2014-10-14T01:54:40.425Z", - "id": "50388077461373184", - "bucket": "default", - "type": 0, - "amount": 5 - }, - "event" : { - "name": "event name", - "metadata": { your event metadata if present } - }, - "referrer": "12345678", - "referree": null - }, - { - "transaction": { - "date": "2014-10-14T01:55:09.474Z", - "id": "50388199301710081", - "bucket": "default", - "type": 2, - "amount": -3 - }, - "event" : { - "name": "event name", - "metadata": { your event metadata if present } - }, - "referrer": null, - "referree": "12345678" - } -] -``` -**referrer** : The id of the referring user for this credit transaction. Returns null if no referrer is involved. Note this id is the user id in a developer's own system that's previously passed to Branch's identify user API call. - -**referree** : The id of the user who was referred for this credit transaction. Returns null if no referree is involved. Note this id is the user id in a developer's own system that's previously passed to Branch's identify user API call. - -**type** : This is the type of credit transaction. - -* 0 - A reward that was added automatically by the user completing an action or promo. -* 1 - A reward that was added manually. -* 2 - A redemption of credits that occurred through our API or SDKs. -* 3 - This is a very unique case where we will subtract credits automatically when we detect fraud. - - -# Getting Started -See the `example` directory for a complete sample app using Branch SDK. - -![Example app](https://user-images.githubusercontent.com/17687286/74096674-725c4200-4ae0-11ea-8ef6-94bc02e1913b.png) - -# Branch Universal Object best practices - -Here are a set of best practices to ensure that your analytics are correct, and your content is ranking on Spotlight effectively. - -1. Set the canonicalIdentifier to a unique, de-duped value across instances of the app -2. Ensure that the title, contentDescription and imageUrl properly represent the object -3. Initialize the Branch Universal Object and call `FlutterBranchSdk.registerView(buo: buo);` on `initState()` -4. Call `showShareSheet` and `getShortUrl` later in the life cycle, when the user takes an action that needs a link -5. Call the additional object events (purchase, share completed, etc) when the corresponding user action is taken - -Practices to avoid: - -1. Don't set the same title, contentDescription and imageUrl across all objects. -2. Don't wait to initialize the object and register views until the user goes to share. -3. Don't wait to initialize the object until you conveniently need a link. -4. Don't create many objects at once and register views in a for loop. - -# Branch Documentation -Read the iOS or Android documentation for all Branch object parameters -* Android - [https://help.branch.io/developers-hub/docs/android-advanced-features](https://help.branch.io/developers-hub/docs/android-advanced-features) -* iOS - [https://help.branch.io/developers-hub/docs/ios-advanced-features](https://help.branch.io/developers-hub/docs/ios-advanced-features) - -# Author -This project was authored by Rodrigo S. Marques. You can contact me at rodrigosmarques@gmail.com -