diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a6a03df4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,40 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: 'Please replace with a clear and descriptive title' +labels: 'type: feature' +assignees: '' +--- + + + +**Which problem is this feature request solving?** + + + +**Describe the solution you'd like** + + + +**Describe alternatives you've considered** + + + +**Can you submit a pull request?** + +Yes/No. + + diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 96acc654..f2f2eed5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -4,6 +4,8 @@ on: push: branches: - "master" + - "dev" + - "bugfix" paths-ignore: - "**/README.md" - "docs/**" @@ -11,6 +13,7 @@ on: branches: - "master" - "dev" + - "bugfix" jobs: check-lint-flutter: @@ -19,20 +22,20 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup flutter uses: subosito/flutter-action@v2 with: cache: true - name: Lint - run: flutter format --output=none --set-exit-if-changed lib/ + run: dart 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 + uses: actions/checkout@v4 - name: Setup flutter uses: subosito/flutter-action@v2 with: @@ -40,4 +43,4 @@ jobs: - name: Install dependencies run: flutter pub get - name: Analyze code - run: flutter analyze --no-fatal-infos \ No newline at end of file + run: flutter analyze --no-fatal-infos diff --git a/.github/workflows/deploy-apk.yaml b/.github/workflows/deploy-apk.yaml index 2a96c1df..cf07e2e5 100644 --- a/.github/workflows/deploy-apk.yaml +++ b/.github/workflows/deploy-apk.yaml @@ -19,13 +19,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-java@v1 + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 with: - java-version: "12.x" - - uses: subosito/flutter-action@v1 - with: - channel: "stable" + java-version: "17.x" + distribution: "zulu" - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -35,7 +34,8 @@ jobs: run: chmod +x tool/build-apk.sh - name: Install dependencies & Build apk run: tool/build-apk.sh - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: name: release-apk - path: ./example/build/app/outputs/apk/release/app-release.apk \ No newline at end of file + path: ./example/build/app/outputs/apk/release/app-release.apk + if-no-files-found: error diff --git a/.github/workflows/deploy-web.yaml b/.github/workflows/deploy-web.yaml index cdfab311..cc83032c 100644 --- a/.github/workflows/deploy-web.yaml +++ b/.github/workflows/deploy-web.yaml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -31,7 +31,7 @@ jobs: run: tool/build-web.sh - name: Deploy to Netlify if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} - uses: nwtgck/actions-netlify@v1.1 + uses: nwtgck/actions-netlify@v3 with: publish-dir: './example/build/web' production-branch: master @@ -44,4 +44,4 @@ jobs: env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} - timeout-minutes: 1 \ No newline at end of file + timeout-minutes: 1 diff --git a/.github/workflows/pr-bugfix-dev.yaml b/.github/workflows/pr-bugfix-dev.yaml index 6b277748..691d8784 100644 --- a/.github/workflows/pr-bugfix-dev.yaml +++ b/.github/workflows/pr-bugfix-dev.yaml @@ -9,10 +9,10 @@ jobs: pull-request: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: pull-request-master-to-bugfix if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} - uses: repo-sync/pull-request@v2.6.2 + uses: repo-sync/pull-request@v2 with: source_branch: "bugfix" destination_branch: "dev" @@ -21,4 +21,4 @@ jobs: pr_label: "auto-pr" pr_draft: false pr_allow_empty: false - github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-master-bugfix.yaml b/.github/workflows/pr-master-bugfix.yaml index ca07a7d2..3256788e 100644 --- a/.github/workflows/pr-master-bugfix.yaml +++ b/.github/workflows/pr-master-bugfix.yaml @@ -9,10 +9,10 @@ jobs: pull-request: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: pull-request-master-to-bugfix if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} - uses: repo-sync/pull-request@v2.6.2 + uses: repo-sync/pull-request@v2 with: source_branch: "master" destination_branch: "bugfix" @@ -21,4 +21,4 @@ jobs: pr_label: "auto-pr" pr_draft: false pr_allow_empty: false - github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-plugin.yml b/.github/workflows/publish-plugin.yml index 1392f5fb..57cc64a0 100644 --- a/.github/workflows/publish-plugin.yml +++ b/.github/workflows/publish-plugin.yml @@ -2,34 +2,28 @@ name: Publish plugin - pub.dev on: push: -# branches: -# - "dev" tags: - 'v*' workflow_dispatch: jobs: - release: + publish: + permissions: + id-token: write # Required for authentication using OIDC runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 - + uses: actions/checkout@v4 + - uses: dart-lang/setup-dart@v1 - name: Setup Flutter uses: subosito/flutter-action@v2 with: channel: "stable" cache: true - + - name: Install dependencies + run: dart pub get + - name: Generate documentation + run: dart doc - 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 - + #run: dart pub publish --dry-run + run: dart pub publish --force diff --git a/.github/workflows/send-dispatch.yaml b/.github/workflows/send-dispatch.yaml index 9d2e73a7..5b7c6d18 100644 --- a/.github/workflows/send-dispatch.yaml +++ b/.github/workflows/send-dispatch.yaml @@ -15,7 +15,7 @@ jobs: steps: - name: Repository Dispatch if: ${{ github.repository == 'RodrigoSMarques/flutter_branch_sdk' }} - uses: peter-evans/repository-dispatch@v2 + uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.TOKEN_BRANCH_UPSTREAM}} repository: BranchMetrics/flutter_branch_sdk diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9676d5..46db99ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,192 @@ +## 8.0.4 +### ⚠️ BREAKING CHANGE +This is a major release which contains breaking API changes. +#### ⚠️ SDK Initialization Changed +* `useTestKey` parameter is no longer supported at `FlutterBranchSdk.init()`. + +Check the instructions in `README.MD` on how to activate the `key_test_`. + +### 🐛 Bug Fixes +* Fix issue #347: ios plugin v8.0.3 crashes when no url is returned +* Fix issue #338: Changing the return value in didFinishLaunchingWithOptions crashes the application from SDK version above 8.0.0 + +## 8.0.3 +### ⚠️ BREAKING CHANGE +This is a major release which contains breaking API changes. +#### ⚠️ SDK Initialization Changed +* `useTestKey` parameter is no longer supported at `FlutterBranchSdk.init()`. + +Check the instructions in `README.MD` on how to activate the `key_test_`. + +### 🐛 Bug Fixes +* Fix issue #340: Logging not working in Android Studio Emulator + +## 8.0.2 +### ⚠️ BREAKING CHANGE +This is a major release which contains breaking API changes. +#### ⚠️ SDK Initialization Changed +* `useTestKey` parameter is no longer supported at `FlutterBranchSdk.init()`. + +Check the instructions in `README.MD` on how to activate the `key_test_`. + +### 🐛 Bug Fixes +* Fix Enable and Disable Tracking on `FlutterBranchSdk.init()` method + +## 8.0.1 +### ⚠️ BREAKING CHANGE +This is a major release which contains breaking API changes. +#### ⚠️ SDK Initialization Changed +* `useTestKey` parameter is no longer supported at `FlutterBranchSdk.init()`. + + Check the instructions in `README.MD` on how to activate the `key_test_`. + +### 🐛 Bug Fixes +* Fix issue #325: Android cannot get the opening link (onInitFinished called after clicking on deep link two times) + +## 8.0.0 +### ⚠️ BREAKING CHANGE +This is a major release which contains breaking API changes. +#### ⚠️ SDK Initialization Changed +* `useTestKey` parameter is no longer supported at `FlutterBranchSdk.init()`. + + Check the instructions in `README.MD` on how to activate the `key_test_`. + +### 🐛 Bug Fixes +* Fix issue #283: Android app not getting correct deeplink from Branch when app is opened +* Fix issue #308: Android non branch deep link sometimes not available +* Fix issue #309: Completion of await FlutterBranchSdk.init() doesn't mean native iOS plugin is ready? +* Fix issue #311: Flutter SDK init falls into loop when race condition happens during the initialization. +* Fix issue #314: Issue with Branch.io Integration on Apple 14 pro +* Fix issue #316: Not getting link after fresh install + +### 🔧 Native SDK Updates + +* Updated included iOS SDK to 3.4.3 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) + +* Updated included Branch Android SDK to 5.12.0 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + + +## 7.3.0 +### 🔧 Native SDK Updates + +* Updated included Branch Android SDK to 5.11.0 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) +* Updated included Branch iOS SDK to 3.4.1 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) + +## 7.2.0 +### 🎉 Features +* `showShareSheet` method will now display the native Android share sheet. +* Documentation review + +### 🔧 Dependencies Update +* Updated dependency `js`. From version 0.6.7 to 7.0.0 + +### 🔧 Native SDK Updates +* Updated included Branch Android SDK to 5.10.1 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + +## 7.1.0 +### 🎉 Features +* New Methods: + - `setDMAParamsForEEA` - In response to the European Union's enactment of the Digital Markets Act (DMA), this new method help pass consent information from your user to Google. + See [documentation](https://github.com/RodrigoSMarques/flutter_branch_sdk?tab=readme-ov-file#user-data) for details. + +### 🐛 Issues + +* Fix issue #297: Allow Call setRequestMetadata after FlutterBranchSdk.init() method + +### 🔧 Native SDK Updates + +* Updated included Branch Android SDK to 5.9.0 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) +* Updated included Branch iOS SDK to 3.3.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) + +## 7.0.4 +### Issues + +* Fix issue #289 : reverts `js` dependency update. From version 0.7.0 to 0.6.7 + +## 7.0.3 +### Issues + +* Fix issue #277 : PlatformException - NullPointerException + +### Features +* PR #286 : fix: export platform_interface +* Updated configuration steps in README.MD +* Sample app - code review + +## 7.0.2 +### Issues + +* Fix issue #261 / #266 / #268: Calling startActivity() from outside of an Activity +* Fix issue #264: Android (PlayStore) : Branch SDK Params empty on background state +* Fix issue #265: New release 7.0.0+ not getting a deeplink data on first launch, when app is on resume +* Fix issue #270: Indicate when error is thrown in init + +### Native SDK Updates + +* Updated included Android SDK to 5.8.0 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + +## 7.0.1 +* Fix issue #255: `Attempt to invoke virtual method 'int android.content.Intent.getFlags()' on a null object reference` when using FlutterFragmentActivity +* Fix issue #256: `A problem occurred configuring project ':flutter_branch_sdk'.` - Add compatibility with AGP 8 (Android Gradle Plugin) + +## 7.0.0 +⚠️ This is a major release which contains breaking API changes. +### BREAKING CHANGE + +* Minimum required Dart SDK version to 2.18 (Flutter 3.3.0) +* Xcode 15 is the min version +* iOS 12 is the min version + +#### SDK Initialization Required +* Use `FlutterBranchSdk.init()` method to initialize the SDK. + +Initialization must be called from `main` or at any time (for example after getting consent for GPDR). + +```dart + await FlutterBranchSdk.init( + useTestKey: false, enableLogging: false, disableTracking: false); +``` + +Check additional instructions in the README + +#### Deprecated / Removed + +* `FlutterBranchSdk.initSession()`. Use `FlutterBranchSdk.listSession()`. +* Removed `setIOSSKAdNetworkMaxTime` method +* Removed Facebook App Install Ads on iOS + +### Features + +* Issue #244 - Support for setting customer_event_alias for BranchEvent +* Updated compile & target SDK to Android API 33. +* Updated example app Android compileSdkVersion to 33. + +### Native SDK Updates + +* Updated included iOS SDK to 3.0.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) + +## 6.9.0 +### Enhancement +* Issue #244 - Support for setting customer_event_alias for BranchEvent + +## 6.8.0 +* Updated Native `Android` SDKs: + * Android Native SDK Update 5.7.+ - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + * iOS Native SDK Update 2.2.1 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) +* Removed: + - Facebook App Install Ads on Android (parameter `branch_enable_facebook_ads`) + +## 6.7.1 +* Fix issue #237: `Pass long URL when try creating Short URL in Offline` + +## 6.7.0 +* Updated Native `Android` and `iOS` SDKs: + * Android Native SDK Update 5.6.+ - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) + * iOS Native SDK Update 2.2.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) +* Removed: + - Firebase App Indexing in Android (`listOnSearch` and `removeFromSearch` return `success` but do not perform any action) + - Old Apple Search Ads APIs (parameter `branch_check_apple_ads`) + ## 6.6.0 * Updated Native `Android` and `iOS` SDKs: * Android Native SDK Update 5.4.+ - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) @@ -98,9 +287,7 @@ ------------ * Updated Native `Android` and `iOS` SDKs: - * Android Native SDK Update 5.1.0 - [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) - * iOS Native SDK Update 1.41.0 - [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) ## 4.0.0 diff --git a/README.md b/README.md index adf47211..319f2e27 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,16 @@ [![Branch](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/branch.png?raw=true)](https://branch.io) +[![Plugin code analysis](https://github.com/RodrigoSMarques/flutter_branch_sdk/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/RodrigoSMarques/flutter_branch_sdk/actions/workflows/ci.yaml) + This is a Flutter plugin that implemented [Branch SDK](https://branch.io). Branch.io helps mobile apps grow with deep links that power referral systems, sharing links and invites with full attribution and analytics. Supports Android, iOS and Web. -* Android - Branch SDK Version >= 5.4.+ [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) -* iOS - Branch SDK Version >= 2.1.+ [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) +* Android - Branch SDK Version >= 5.12.0 [Android Version History](https://github.com/BranchMetrics/android-branch-deep-linking-attribution/releases) +* iOS - Branch SDK Version >= 3.4.3 [iOS Version History](https://github.com/BranchMetrics/ios-branch-deep-linking-attribution/releases) Implemented functions in plugin: @@ -21,7 +23,7 @@ Enable / Disable User Tracking | X | X | X Get First and Last Parameters | X | X | X Generate Deep Link for Branch Universal Object (BUO)| X | X | X Show Share Sheet for Branch Universal Object (BUO)| X | X | X -List BUO on Search / Remove BUO from Search| X | X | Not supported +List BUO on Search / Remove BUO from Search| | X | Register view| X | X | X Track User Actions and Events| X | X | X Init Branch Session and Deep Link| X | X | X @@ -33,29 +35,38 @@ Handle Links in Your Own App| X | X | X ## Getting Started ### Configure Branch Dashboard * Register Your App -* Complete the Basic integration in [Branch Dashboard](https://dashboard.branch.io/login) +* Configure Branch Dashboard [Branch Dashboard](https://dashboard.branch.io/login) For details see: -* [iOS - only section: **Configure Branch**](https://help.branch.io/developers-hub/docs/ios-basic-integration#configure-branch) -* [Android - only section: **Configure Branch Dashboard**](https://help.branch.io/developers-hub/docs/android-basic-integration#configure-branch-dashboard) +* [iOS: only section: **Configure Branch Dashboard**](https://help.branch.io/developers-hub/docs/ios-basic-integration#1-configure-branch-dashboard) +* [Android - only section: **Configure Branch Dashboard**](https://help.branch.io/developers-hub/docs/android-basic-integration#1-configure-branch-dashboard) ## Configure Platform Project ### Android Integration -Follow the steps on the page [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` +Follow only the steps: + +* [Configure App](https://help.branch.io/developers-hub/docs/android-basic-integration#4-configure-app) +* [Configure ProGuard](https://help.branch.io/developers-hub/docs/android-basic-integration#7-configure-proguard) + +**Note**: It is not necessary to perform the Branch Android SDK installation steps. The plugin performs these steps. ### iOS Integration -Follow the steps on the page [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 +Follow only the steps: + +* [Configure bundle identifier](https://help.branch.io/developers-hub/docs/ios-basic-integration#2-configure-bundle-identifier) +* [Configure associated domains](https://help.branch.io/developers-hub/docs/ios-basic-integration#3-configure-associated-domains) +* [Configure Info.plist](https://help.branch.io/developers-hub/docs/ios-basic-integration#4-configure-infoplist) -> Note: In `Info.plist` not add `branch_key` `live` and `test` at the same time.
-Use only `branch_key` and update as needed. +**Note**: It is not necessary to perform the Branch iOS SDK installation steps. The plugin performs these steps. + +#### NativeLink™ Deferred Deep Linking +Use iOS pasteboard to enable deferred deep linking via Branch NativeLink™, which enables 100% matching on iOS through Installs. + +Follow the steps on the [page](https://help.branch.io/developers-hub/docs/ios-advanced-features#nativelink-deferred-deep-linking), session _**NativeLink™ Deferred Deep Linking**_, + +**Note**: Code implementation in Swift is not necessary. The plugin already implements the code, requiring only configuration on the Dashboard. ### Web Integration @@ -63,11 +74,13 @@ You need add Branch Javascript in your `web\index.html` at the top of your ` - (function(b,r,a,n,c,h,_,s,d,k){if(!b[n]||!b[n]._q){for(;s<_.length;)c(h,_[s++]);d=r.createElement(a);d.async=1;d.src="https://cdn.branch.io/branch-latest.min.js";k=r.getElementsByTagName(a)[0];k.parentNode.insertBefore(d,k);b[n]=h}})(window,document,"script","branch",function(b,r){b[r]=function(){b._q.push([r,arguments])}},{_q:[],_v:1},"addListener applyCode autoAppIndex banner closeBanner closeJourney creditHistory credits data deepview deepviewCta first getCode init link logout redeem referrals removeListener sendSMS setBranchViewData setIdentity track validateCode trackCommerceEvent logEvent disableTracking".split(" "), 0); - branch.init('key_live_YOUR_KEY_GOES_HERE'); + // load Branch + (function(b,r,a,n,c,h,_,s,d,k){if(!b[n]||!b[n]._q){for(;s<_.length;)c(h,_[s++]);d=r.createElement(a);d.async=1;d.src="https://cdn.branch.io/branch-latest.min.js";k=r.getElementsByTagName(a)[0];k.parentNode.insertBefore(d,k);b[n]=h}})(window,document,"script","branch",function(b,r){b[r]=function(){b._q.push([r,arguments])}},{_q:[],_v:1},"addListener banner closeBanner closeJourney data deepview deepviewCta first init link logout removeListener setBranchViewData setIdentity track trackCommerceEvent logEvent disableTracking getBrowserFingerprintId crossPlatformIds lastAttributedTouchData setAPIResponseCallback qrCode setRequestMetaData setAPIUrl getAPIUrl setDMAParamsForEEA".split(" "), 0); + // init Branch + branch.init('key_live_or_test_YOUR_KEY_GOES_HERE'); ``` -Change `key_live_YOUR_KEY_GOES_HERE` to match your [Branch Dashboard](https://dashboard.branch.io/account-settings/app) +Change `key_live_or_test_YOUR_KEY_GOES_HERE ` to match your [Branch Dashboard](https://dashboard.branch.io/account-settings/app) If `branch.init()` fails, all subsequent Branch methods will fail. @@ -107,8 +120,10 @@ Full example `index.html`: + ``` -### Facebook App Install Ads - -Branch links can be used together with Facebook App Install Campaign ads, allowing you to track ad-driven installs on the Branch dashboard and deep link those new users directly to content the first time they open your app. - -Follow the instructions on the link -https://help.branch.io/using-branch/docs/facebook-app-install-ads. - +***Note***: Remember to set the value to `false` before releasing to production. +### iOS -To read Facebook App Install deep links update `INFO.PLIST` on `iOS` or `AndroidManifest.xml` on `Android` as in the example: +1) Create an empty file called `branch.json`. -For `iOS` add to `INFO.PLIST`: +2) Paste the content below into the file or make download [here](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/branch.json): -```swift - branch_enable_facebook_ads - -``` - -For `Android` add to `AndroidManifest.xml`: +```json +{ + "useTestInstance": true +} -```java - ``` -Follow the instructions to install Facebook Android / iOS SDK: - -`iOS`: - -https://developers.facebook.com/docs/ios/use-cocoapods +3) Add the file `branch.json` to your project using Xcode. Within your project, navigate to File → Add Files. -`Android`: +4) Select the `branch.json` file and make sure every target in your project that uses Branch is selected. -https://developers.facebook.com/docs/android/getting-started +![branch.json](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/branch_json_add.png) +![branch.json](https://github.com/RodrigoSMarques/flutter_branch_sdk/blob/master/assets/branch_json_project.png) +**Note*:* Remember to set the value to `false` before releasing to production. # Getting Started See the `example` directory for a complete sample app using Branch SDK. diff --git a/android/build.gradle b/android/build.gradle index d45c87ff..ed301c1b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,13 @@ group 'br.com.rsmarques.flutter_branch_sdk' version '1.0' +def getPackageVersion() { + def props = new Properties() + file('../pubspec.yaml').withInputStream { props.load(it) } + println props.getProperty("version") + props.getProperty("version") +} + buildscript { repositories { google() @@ -8,7 +15,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.3.1' } } @@ -22,23 +29,31 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + // Conditional for compatibility with AGP <4.2. + if (project.android.hasProperty("namespace")) { + namespace 'br.com.rsmarques.flutter_branch_sdk' + } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + buildFeatures { + buildConfig = true } defaultConfig { minSdkVersion 21 + compileSdk 34 + buildConfigField("String", "FBRANCH_VERSION", "\"${getPackageVersion()}\"") + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { - implementation 'io.branch.sdk.android:library:5.4.+' - implementation 'com.google.firebase:firebase-appindexing:19.0.0' - implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1+' - implementation 'androidx.browser:browser:1.4.0' - implementation 'androidx.lifecycle:lifecycle-runtime:2.5.1' - implementation 'androidx.browser:browser:1.4.0' + implementation 'io.branch.sdk.android:library:5.12.+' + implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' + implementation 'androidx.lifecycle:lifecycle-runtime:2.7.0' + implementation 'androidx.browser:browser:1.8.0' + implementation "store.galaxy.samsung.installreferrer:samsung_galaxystore_install_referrer:4.0.0" } \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..98c925c3 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index 989ef1a9..7d49f31d 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1 +1,2 @@ rootProject.name = 'flutter_branch_sdk' +android.defaults.buildfeatures.buildconfig=true diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index e0c7b550..20105733 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,4 +1,11 @@ + + + + + + + diff --git a/android/src/main/assets/branch.json b/android/src/main/assets/branch.json new file mode 100644 index 00000000..3cba7976 --- /dev/null +++ b/android/src/main/assets/branch.json @@ -0,0 +1,3 @@ +{ + "deferInitForPluginRuntime": true +} \ No newline at end of file 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 deleted file mode 100644 index 532cb9f8..00000000 --- a/android/src/main/java/br/com/rsmarques/flutter_branch_sdk/ApplicationInfoHelper.java +++ /dev/null @@ -1,50 +0,0 @@ -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 f5be7455..d5a8af5c 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 @@ -176,7 +176,6 @@ BranchEvent convertToEvent(HashMap eventMap) { } else { event = new BranchEvent((String) eventMap.get("eventName")); } - if (eventMap.containsKey("transactionID")) event.setTransactionID((String) eventMap.get("transactionID")); if (eventMap.containsKey("currency")) @@ -202,6 +201,9 @@ BranchEvent convertToEvent(HashMap eventMap) { event.addCustomDataProperty(customData.getKey(), customData.getValue()); } } + if (eventMap.containsKey("alias")) { + event.setCustomerEventAlias((String) eventMap.get("alias")); + } return event; } 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 281d34a7..045a4be4 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,18 @@ -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.4.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); - } -} +package br.com.rsmarques.flutter_branch_sdk; + +import android.content.Context; + +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 = "8.0.0"; + + public static void init(Context context) { + LogUtils.debug(DEBUG_NAME, "SDK Init"); + Branch.expectDelayedSessionInitialization(true); + Branch.registerPlugin(PLUGIN_NAME, br.com.rsmarques.flutter_branch_sdk.BuildConfig.FBRANCH_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 ad60294f..ea1a6bd1 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 @@ -4,23 +4,27 @@ import android.app.Application; import android.content.Context; import android.content.Intent; +import android.os.Build; 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.Iterator; 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.BranchLogger; import io.branch.referral.QRCode.BranchQRCode; import io.branch.referral.ServerRequestGetLATD; import io.branch.referral.util.BranchEvent; @@ -43,826 +47,945 @@ 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().getData()).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().getData()).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) { - return false; - } - if (intent == null) { - return false; - } - Intent newIntent = intent; - if (!intent.hasExtra("branch_force_new_session")) { - newIntent.putExtra("branch_force_new_session",true); - } - this.activity.setIntent(newIntent); - Branch.sessionBuilder(this.activity).withCallback(branchReferralInitListener).reInit(); - return true; - } - - /** - * --------------------------------------------------------------------------------------------- - * 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 "shareWithLPLinkMetadata": - 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; - case "handleDeepLink": - handleDeepLink(call); - break; - case "addFacebookPartnerParameter": - addFacebookPartnerParameter(call); - break; - case "clearPartnerParameters" : - clearPartnerParameters(); - break; - case "setPreinstallCampaign" : - setPreinstallCampaign(call); - break; - case "setPreinstallPartner" : - setPreinstallPartner(call); - break; - case "addSnapPartnerParameter" : - addSnapPartnerParameter(call); - 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()); + private static final String DEBUG_NAME = "FlutterBranchSDK"; + private static final String MESSAGE_CHANNEL = "flutter_branch_sdk/message"; + private static final String EVENT_CHANNEL = "flutter_branch_sdk/event"; + private Activity activity; + private Context context; + private ActivityPluginBinding activityPluginBinding; + private EventSink eventSink = null; + private Map sessionParams = null; + private BranchError initialError = null; + private final FlutterBranchSdkHelper branchSdkHelper = new FlutterBranchSdkHelper(); + private final JSONObject requestMetadata = new JSONObject(); + private final JSONObject facebookParameters = new JSONObject(); + private final JSONObject snapParameters = new JSONObject(); + private final ArrayList preInstallParameters = new ArrayList(); + private final ArrayList campaingParameters = new ArrayList(); + private boolean isInitialized = false; + + /** + * --------------------------------------------------------------------------------------------- + * Plugin registry + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + LogUtils.debug(DEBUG_NAME, "triggered onAttachedToEngine"); + setupChannels(binding.getBinaryMessenger(), binding.getApplicationContext()); + } + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + LogUtils.debug(DEBUG_NAME, "triggered onDetachedFromEngine"); + teardownChannels(); + } + + private void setupChannels(BinaryMessenger messenger, Context context) { + LogUtils.debug(DEBUG_NAME, "triggered setupChannels"); + 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, "triggered setActivity"); + + this.activity = activity; + activity.getApplication().registerActivityLifecycleCallbacks(this); + + if (this.activity != null && FlutterFragmentActivity.class.isAssignableFrom(activity.getClass())) { + Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent().getData()).init(); + } + } + + private void teardownChannels() { + LogUtils.debug(DEBUG_NAME, "triggered teardownChannels"); + this.activityPluginBinding = null; + this.activity = null; + this.context = null; + } + + /** + * --------------------------------------------------------------------------------------------- + * ActivityAware Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) { + LogUtils.debug(DEBUG_NAME, "triggered onAttachedToActivity"); + this.activityPluginBinding = activityPluginBinding; + setActivity(activityPluginBinding.getActivity()); + activityPluginBinding.addOnNewIntentListener(this); + } + + @Override + public void onDetachedFromActivity() { + LogUtils.debug(DEBUG_NAME, "triggered onDetachedFromActivity"); + activityPluginBinding.removeOnNewIntentListener(this); + this.activity = null; + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + LogUtils.debug(DEBUG_NAME, "triggered onDetachedFromActivityForConfigChanges"); + onDetachedFromActivity(); + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) { + LogUtils.debug(DEBUG_NAME, "triggered onReattachedToActivityForConfigChanges"); + onAttachedToActivity(activityPluginBinding); + } + + /** + * --------------------------------------------------------------------------------------------- + * StreamHandler Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onListen(Object o, EventChannel.EventSink eventSink) { + LogUtils.debug(DEBUG_NAME, "triggered onListen"); + this.eventSink = new MainThreadEventSink(eventSink); + if (sessionParams != null) { + eventSink.success(sessionParams); + sessionParams = null; + initialError = null; + } else if (initialError != null) { + eventSink.error(String.valueOf(initialError.getErrorCode()), initialError.getMessage(), null); + sessionParams = null; + initialError = null; + } + } + + @Override + public void onCancel(Object o) { + LogUtils.debug(DEBUG_NAME, "triggered onCancel"); + this.eventSink = new MainThreadEventSink(null); + initialError = null; + sessionParams = null; + } + + /** + * --------------------------------------------------------------------------------------------- + * ActivityLifecycleCallbacks Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onActivityCreated(@NonNull Activity activity, Bundle bundle) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityCreated: " + activity.getClass().getName()); + } + + @Override + public void onActivityStarted(@NonNull Activity activity) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityStarted: " + activity.getClass().getName()); + if (this.activity != activity) { + return; + } + LogUtils.debug(DEBUG_NAME, "triggered SessionBuilder init"); + Branch.sessionBuilder(activity).withCallback(branchReferralInitListener).withData(activity.getIntent().getData()).init(); + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityResumed: " + activity.getClass().getName()); + } + + @Override + public void onActivityPaused(@NonNull Activity activity) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityPaused: " + activity.getClass().getName()); + // Delay session initialization + Branch.expectDelayedSessionInitialization(true); + } + + @Override + public void onActivityStopped(@NonNull Activity activity) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityStopped: " + activity.getClass().getName()); + } + + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) { + } + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + LogUtils.debug(DEBUG_NAME, "triggered onActivityDestroyed: " + activity.getClass().getName()); + if (this.activity == activity) { + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + } + } + + /** + * --------------------------------------------------------------------------------------------- + * NewIntentListener Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public boolean onNewIntent(@NonNull Intent intent) { + LogUtils.debug(DEBUG_NAME, "triggered onNewIntent"); + if (this.activity == null) { + return false; + } + this.activity.setIntent(intent); + if (intent.hasExtra("branch_force_new_session") && intent.getBooleanExtra("branch_force_new_session",false)) { + Branch.sessionBuilder(this.activity).withCallback(branchReferralInitListener).reInit(); + LogUtils.debug(DEBUG_NAME, "triggered SessionBuilder reInit"); + } + return true; + } + + /** + * --------------------------------------------------------------------------------------------- + * MethodCallHandler Interface Methods + * -------------------------------------------------------------------------------------------- + **/ + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result rawResult) { + Result result = new MethodResultWrapper(rawResult); + switch (call.method) { + case "init": + setupBranch(call, result); + break; + case "getShortUrl": + getShortUrl(call, result); + break; + case "shareWithLPLinkMetadata": + 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; + case "handleDeepLink": + handleDeepLink(call); + break; + case "addFacebookPartnerParameter": + addFacebookPartnerParameter(call); + break; + case "clearPartnerParameters": + clearPartnerParameters(); + break; + case "setPreinstallCampaign": + setPreinstallCampaign(call); + break; + case "setPreinstallPartner": + setPreinstallPartner(call); + break; + case "addSnapPartnerParameter": + addSnapPartnerParameter(call); + break; + case "setDMAParamsForEEA": + setDMAParamsForEEA(call); + 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) { + LogUtils.debug(DEBUG_NAME, "triggered onInitFinished"); + if (error == null) { + LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - params: " + params.toString()); + + try { + sessionParams = branchSdkHelper.paramsToMap(params); + } catch (JSONException e) { + LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error to Map: " + e.getLocalizedMessage()); + return; + } + if (eventSink != null) { + eventSink.success(sessionParams); + sessionParams = null; + } + } else { + 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 setupBranch(MethodCall call, final Result result) { + LogUtils.debug(DEBUG_NAME, "triggered setupBranch"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + + if (isInitialized) { + result.success(Boolean.TRUE); + } + + HashMap argsMap = (HashMap) call.arguments; + + if ((Boolean) argsMap.get("enableLogging")) { + Branch.enableLogging(BranchLogger.BranchLogLevel.VERBOSE); + } else { + Branch.disableLogging(); + } + + if (requestMetadata.length() > 0) { + Iterator keys = requestMetadata.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); try { - initialParams = branchSdkHelper.paramsToMap(params); + Branch.getInstance().setRequestMetadata(key, requestMetadata.getString(key)); } catch (JSONException e) { - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error to Map: " + e.getLocalizedMessage()); - return; + // no-op } - if (eventSink != null) { - eventSink.success(initialParams); - initialParams = null; + } + } + if (facebookParameters.length() > 0) { + Iterator keys = facebookParameters.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + try { + Branch.getInstance().addFacebookPartnerParameterWithName(key, facebookParameters.getString(key)); + } catch (JSONException e) { + // no-op } - } else { - if (error.getErrorCode() == BranchError.ERR_BRANCH_ALREADY_INITIALIZED || error.getErrorCode() == BranchError.ERR_IMPROPER_REINITIALIZATION) { - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - warning: " + error); - return; + } + } + if (snapParameters.length() > 0) { + Iterator keys = snapParameters.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + try { + Branch.getInstance().addSnapPartnerParameterWithName(key, snapParameters.getString(key)); + } catch (JSONException e) { + // no-op } - LogUtils.debug(DEBUG_NAME, "BranchReferralInitListener - error: " + error); - if (eventSink != null) { - eventSink.error(String.valueOf(error.getErrorCode()), error.getMessage(), null); - initialError = null; + } + } + if (!preInstallParameters.isEmpty()) { + for (int i = 0; i < preInstallParameters.size(); i++) { + Branch.getAutoInstance(context).setPreinstallPartner(preInstallParameters.get(i)); + } + } + if (!campaingParameters.isEmpty()) { + for (int i = 0; i < campaingParameters.size(); i++) { + Branch.getAutoInstance(context).setPreinstallCampaign(campaingParameters.get(i)); + } + } + if ((Boolean) argsMap.get("disableTracking")) { + Branch.getInstance().disableTracking(true); + } else { + Branch.getInstance().disableTracking(false); + } + + LogUtils.debug(DEBUG_NAME, "notifyNativeToInit()"); + Branch.notifyNativeToInit(); + isInitialized = true; + result.success(Boolean.TRUE); + } + + 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 && url != null) || (error != null && url != null)) { + LogUtils.debug(DEBUG_NAME, "Branch link to share: " + url); + response.put("success", true); + response.put("url", url); } else { - initialError = error; + 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<>(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + + Branch.getInstance().share(activity, buo, linkProperties, new Branch.BranchNativeLinkShareListener() { + @Override + public void onLinkShareResponse(String sharedLink, 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) { + LogUtils.debug(DEBUG_NAME, "Branch link share channel: " + channelName); + } + }, + messageTitle, + messageText); + } else { + 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, "triggered registerView"); + 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 validateSDKIntegration() { - IntegrationValidator.validate(activity); - } + private void listOnSearch(MethodCall call, Result result) { + LogUtils.debug(DEBUG_NAME, "triggered listOnSearch"); + 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 getShortUrl(MethodCall call, final Result result) { - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); + private void removeFromSearch(MethodCall call, Result result) { + LogUtils.debug(DEBUG_NAME, "triggered removeFromSearch"); + 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); } - HashMap argsMap = (HashMap) call.arguments; - BranchUniversalObject buo = branchSdkHelper.convertToBUO((HashMap) argsMap.get("buo")); + private void trackContent(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered trackContent"); + 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); + } + }); + } - LinkProperties linkProperties = branchSdkHelper.convertToLinkProperties((HashMap) argsMap.get("lp")); + private void trackContentWithoutBuo(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered trackContentWithoutBuo"); + 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); + } + }); + } - final Map response = new HashMap<>(); + private void setIdentity(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setIdentity"); + 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.getInstance().setIdentity(userId); + } + }); + } - buo.generateShortUrl(activity, linkProperties, new Branch.BranchLinkCreateListener() { - @Override - public void onLinkCreate(String url, BranchError error) { + private void setRequestMetadata(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setRequestMetadata"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String key = call.argument("key"); + final String value = call.argument("value"); - 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()); + if (requestMetadata.has(key) && value.isEmpty()) { + requestMetadata.remove(key); + } else { + try { + requestMetadata.put(key, value); + } catch (JSONException error) { } - result.success(response); - } + return; + } + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getInstance().setRequestMetadata(key, value); + } + }); + } - @Override - public void onChannelSelected(String channelName) { + private void logout() { + LogUtils.debug(DEBUG_NAME, "triggered logout"); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getInstance().logout(); + } + }); + } - } + private void getLatestReferringParams(Result result) { + LogUtils.debug(DEBUG_NAME, "triggered getLatestReferringParams"); + JSONObject sessionParams = Branch.getInstance().getLatestReferringParams(); + try { + result.success(branchSdkHelper.paramsToMap(sessionParams)); + } catch (JSONException e) { + e.printStackTrace(); + result.error(DEBUG_NAME, e.getMessage(), null); + } + } - @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() { + private void getFirstReferringParams(Result result) { + LogUtils.debug(DEBUG_NAME, "triggered getFirstReferringParams"); + JSONObject sessionParams = Branch.getInstance().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, "triggered setTrackingDisabled"); + 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.getInstance().disableTracking(value); + } + }); + } + + private void isUserIdentified(Result result) { + LogUtils.debug(DEBUG_NAME, "triggered isUserIdentified"); + result.success(Branch.getInstance().isUserIdentified()); + } + + private void setConnectTimeout(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setConnectTimeout"); + 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.getInstance().setNetworkConnectTimeout(value); + } + }); + } + + private void setTimeout(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setConnectTimeout"); + 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.getInstance().setNetworkTimeout(value); + } + }); + } + + private void setRetryCount(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setRetryCount"); + 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.getInstance().setRetryCount(value); + } + }); + } + + private void setRetryInterval(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setRetryInterval"); + 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.getInstance().setRetryInterval(value); + } + }); + } + + private void getLastAttributedTouchData(final MethodCall call, final Result result) { + LogUtils.debug(DEBUG_NAME, "triggered getLastAttributedTouchData"); + final Map response = new HashMap<>(); + if (call.hasArgument("attributionWindow")) { + final int attributionWindow = call.argument("attributionWindow"); + Branch.getInstance().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.getInstance().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, "triggered getQRCodeAsData"); + 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 onDataFetched(JSONObject jsonObject, BranchError error) { - if (error == null) { + public void onSuccess(byte[] qrCodeData) { + 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); + response.put("result", qrCodeData); + 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 { + public void onFailure(Exception error) { response.put("success", Boolean.FALSE); - response.put("errorCode", String.valueOf(error.getErrorCode())); + response.put("errorCode", "-1"); response.put("errorMessage", error.getMessage()); - } - result.success(response); + result.success(response); } - }); + }); + } catch (IOException e) { + response.put("success", Boolean.FALSE); + response.put("errorCode", "-1"); + response.put("errorMessage", e.getMessage()); + result.success(response); + } + } + + private void handleDeepLink(final MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered handleDeepLink"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String url = call.argument("url"); + Intent intent = new Intent(context, activity.getClass()); + intent.putExtra("branch", url); + intent.putExtra("branch_force_new_session", true); + activity.startActivity(intent); + } + + private void addFacebookPartnerParameter(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered addFacebookPartnerParameter"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String key = call.argument("key"); + final String value = call.argument("value"); + if (facebookParameters.has(key) && value.isEmpty()) { + facebookParameters.remove(key); + } else { + try { + facebookParameters.put(key, value); + } catch (JSONException error) { + } + } + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).addFacebookPartnerParameterWithName(key, value); + } + }); + } + + private void clearPartnerParameters() { + LogUtils.debug(DEBUG_NAME, "triggered clearPartnerParameters"); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).clearPartnerParameters(); + } + }); } - } - private void getQRCode(final MethodCall call, final Result result) { + private void setPreinstallCampaign(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setPreinstallCampaign"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String value = call.argument("value"); + campaingParameters.add(value); - LogUtils.debug(DEBUG_NAME, "getQRCodeAsData call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setPreinstallCampaign(value); + } + }); } - 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<>(); + private void setPreinstallPartner(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setPreinstallPartner"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String value = call.argument("value"); + preInstallParameters.add(value); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).setPreinstallPartner(value); + } + }); + } - try { - branchQRCode.getQRCodeAsData(context, buo, linkProperties, new BranchQRCode.BranchQRCodeDataHandler() { - @Override - public void onSuccess(byte[] qrCodeData) { + private void addSnapPartnerParameter(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered addSnapPartnerParameter"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final String key = call.argument("key"); + final String value = call.argument("value"); + if (snapParameters.has(key) && value.isEmpty()) { + snapParameters.remove(key); + } else { + try { + snapParameters.put(key, value); + } catch (JSONException error) { + } + } - 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); - } + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Branch.getAutoInstance(context).addSnapPartnerParameterWithName(key, value); + } }); - } catch (IOException e) { - response.put("success", Boolean.FALSE); - response.put("errorCode", "-1"); - response.put("errorMessage", e.getMessage()); - result.success(response); - } - } - - private void handleDeepLink(final MethodCall call) { - - LogUtils.debug(DEBUG_NAME, "handleDeepLink call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - HashMap argsMap = (HashMap) call.arguments; - - final String url = call.argument("url"); - - Intent intent = new Intent(context, activity.getClass()); - intent.putExtra("branch",url); - intent.putExtra("branch_force_new_session",true); - activity.startActivity(intent); - } - - private void addFacebookPartnerParameter(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "addFacebookPartnerParameter 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).addFacebookPartnerParameterWithName(key, value); - } - }); - } - - private void clearPartnerParameters() { - LogUtils.debug(DEBUG_NAME, "clearPartnerParameters call"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).clearPartnerParameters(); - } - }); - } - - private void setPreinstallCampaign(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setPreinstallCampaign call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - - final String value = call.argument("value"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setPreinstallCampaign(value); - } - }); - } - - private void setPreinstallPartner(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "setPreinstallPartner call"); - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - - final String value = call.argument("value"); - - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - Branch.getAutoInstance(context).setPreinstallPartner(value); - } - }); - } - private void addSnapPartnerParameter(MethodCall call) { - LogUtils.debug(DEBUG_NAME, "addSnapPartnerParameter 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).addSnapPartnerParameterWithName(key, value); - } - }); - } + } + + private void setDMAParamsForEEA(MethodCall call) { + LogUtils.debug(DEBUG_NAME, "triggered setDMAParamsForEEA"); + if (!(call.arguments instanceof Map)) { + throw new IllegalArgumentException("Map argument expected"); + } + final boolean eeaRegion = Boolean.TRUE.equals(call.argument("eeaRegion")); + final boolean adPersonalizationConsent = Boolean.TRUE.equals(call.argument("adPersonalizationConsent")); + final boolean adUserDataUsageConsent = Boolean.TRUE.equals(call.argument("adUserDataUsageConsent")); + + Branch.getInstance().setDMAParamsForEEA(eeaRegion,adPersonalizationConsent,adUserDataUsageConsent); + } } diff --git a/assets/branch.json b/assets/branch.json new file mode 100644 index 00000000..c2059b64 --- /dev/null +++ b/assets/branch.json @@ -0,0 +1,3 @@ +{ + "useTestInstance": true +} diff --git a/assets/branch_json_add.png b/assets/branch_json_add.png new file mode 100644 index 00000000..059888fb Binary files /dev/null and b/assets/branch_json_add.png differ diff --git a/assets/branch_json_project.png b/assets/branch_json_project.png new file mode 100644 index 00000000..056bb3be Binary files /dev/null and b/assets/branch_json_project.png differ diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 17024421..4286cce4 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,10 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + namespace 'br.com.rsmarques.flutter_branch_sdk_example' + defaultConfig { + compileSdk flutter.compileSdkVersion + } ndkVersion flutter.ndkVersion compileOptions { diff --git a/example/android/app/proguard-rules.pro b/example/android/app/proguard-rules.pro index 24f63cf8..6a3254af 100644 --- a/example/android/app/proguard-rules.pro +++ b/example/android/app/proguard-rules.pro @@ -1,16 +1 @@ -#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 +-keep class com.google.android.gms.** { *; } \ 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 3cc9bbcf..8ffe0246 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - +