├── .envrc ├── Branch-SDK-TestBed ├── src │ └── main │ │ ├── assets │ │ └── branch.json │ │ ├── res │ │ ├── drawable │ │ │ ├── shadow.png │ │ │ ├── ic_launcher.png │ │ │ ├── pill_button.xml │ │ │ ├── ic_baseline_first_page_24.xml │ │ │ ├── ic_baseline_last_page_24.xml │ │ │ ├── ic_baseline_person_24.xml │ │ │ ├── ic_baseline_insert_photo_24.xml │ │ │ ├── ic_baseline_logout_24.xml │ │ │ ├── ic_baseline_security_24.xml │ │ │ ├── ic_baseline_open_in_new_24.xml │ │ │ ├── baseline_document_scanner_24.xml │ │ │ ├── ic_baseline_doorbell_24.xml │ │ │ ├── ic_baseline_link_24.xml │ │ │ ├── ic_baseline_remove_red_eye_24.xml │ │ │ ├── ic_baseline_emoji_events_24.xml │ │ │ ├── ic_baseline_shopping_bag_24.xml │ │ │ ├── ic_baseline_person_off_24.xml │ │ │ ├── ic_baseline_pageview_24.xml │ │ │ ├── ic_baseline_share_24.xml │ │ │ ├── ic_baseline_monetization_on_24.xml │ │ │ ├── ic_baseline_settings_24.xml │ │ │ └── ic_baseline_qr_code_24.xml │ │ ├── values │ │ │ ├── integers.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── menu │ │ │ └── menu_log_output.xml │ │ ├── xml │ │ │ └── network_security_config.xml │ │ └── layout │ │ │ ├── activity_log_output.xml │ │ │ ├── auto_deep_link_test.xml │ │ │ └── activity_settings.xml │ │ └── java │ │ └── io │ │ └── branch │ │ └── branchandroidtestbed │ │ ├── AutoDeepLinkTestActivity.java │ │ ├── CustomBranchApp.java │ │ └── LogOutputActivity.java ├── lint.xml ├── signing.properties.sample ├── release │ └── Branch-SDK-TestBed-release.aab ├── Branch-SDK-TestBed-release-unsigned.apk ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── project.properties ├── proguard-project.txt ├── build.gradle.kts └── InitializationTestPlan.md ├── Branch-SDK ├── doc │ ├── package-list │ ├── script.js │ ├── index.html │ ├── allclasses-noframe.html │ ├── io │ │ └── branch │ │ │ └── referral │ │ │ ├── package-frame.html │ │ │ └── class-use │ │ │ ├── Base64.html │ │ │ ├── ApkParser.html │ │ │ └── BuildConfig.html │ └── allclasses-frame.html ├── src │ ├── androidTest │ │ ├── assets │ │ │ ├── pre_install_apps_null.branch │ │ │ ├── pre_install_apps.branch │ │ │ └── pre_install_apps_no_package.branch │ │ ├── java │ │ │ └── io │ │ │ │ └── branch │ │ │ │ └── referral │ │ │ │ ├── utils │ │ │ │ └── AssetUtils.java │ │ │ │ ├── KeyTest.java │ │ │ │ ├── test │ │ │ │ └── mock │ │ │ │ │ └── MockActivity.java │ │ │ │ ├── SystemObserverTests.kt │ │ │ │ ├── DebugTest.java │ │ │ │ ├── BillingGooglePlayTests.kt │ │ │ │ ├── BranchPluginSupportTest.java │ │ │ │ ├── AdvertisingIdTests.kt │ │ │ │ ├── BranchCPIDTest.java │ │ │ │ └── BranchTestRequestUtil.java │ │ └── AndroidManifest.xml │ └── main │ │ ├── res │ │ ├── drawable │ │ │ └── branch_icon.png │ │ └── layout │ │ │ ├── linking_validator_dialog_row_item.xml │ │ │ └── integration_validator_dialog_row_item.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── io │ │ └── branch │ │ ├── referral │ │ ├── IBranchRequestTracingCallback.java │ │ ├── util │ │ │ ├── AdType.java │ │ │ ├── DependencyUtils.kt │ │ │ ├── BranchContentSchema.java │ │ │ ├── BRANCH_STANDARD_EVENT.java │ │ │ ├── SharingUtil.kt │ │ │ ├── ProductCategory.java │ │ │ ├── Product.java │ │ │ ├── CurrencyType.java │ │ │ └── CommerceEvent.java │ │ ├── validators │ │ │ ├── IntegrationValidatorCheck.java │ │ │ ├── BranchKeysValidatorCheck.java │ │ │ ├── LinkingValidator.java │ │ │ ├── BranchInstanceCreationValidatorCheck.java │ │ │ ├── PackageNameCheck.java │ │ │ ├── ServerRequestGetAppConfig.java │ │ │ ├── CustomDomainCheck.java │ │ │ ├── DefaultDomainsCheck.java │ │ │ ├── AlternateDomainsCheck.java │ │ │ ├── AppLinksCheck.java │ │ │ ├── IntegrationValidatorConstants.java │ │ │ ├── BranchIntegrationModel.java │ │ │ ├── IntegrationValidatorDialogRowItem.java │ │ │ └── LinkingValidatorConstants.java │ │ ├── BranchApp.java │ │ ├── BranchAsyncTask.java │ │ ├── SharingHelper.java │ │ ├── BranchUniversalReferralInitWrapper.java │ │ ├── BranchPartnerParameters.java │ │ ├── QRCode │ │ │ └── ServerRequestCreateQRCode.java │ │ ├── QueueOperationLogout.kt │ │ ├── ServerRequestGetLATD.java │ │ ├── QueueOperationSetIdentity.kt │ │ ├── ServerRequestLogEvent.java │ │ ├── BranchQRCodeCache.java │ │ ├── BranchPluginSupport.java │ │ └── TrackingController.java │ │ ├── interfaces │ │ └── IBranchLoggingCallbacks.java │ │ ├── data │ │ └── InstallReferrerResult.kt │ │ ├── receivers │ │ └── SharingBroadcastReceiver.kt │ │ └── coroutines │ │ ├── AdvertisingIds.kt │ │ └── DeviceSignals.kt ├── lint.xml ├── gradle.properties ├── proguard-consumer.txt ├── javadoc.xml ├── project.properties └── proguard-project.txt ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature-request.yml │ ├── config.yml │ └── bug-report.yml ├── pull_request_template.md ├── workflows │ ├── stale.yml │ └── gptdriverautomation.yaml └── gptdriverrunscript.sh ├── settings.gradle.kts ├── SECURITY.md ├── .gitattributes ├── .gitignore ├── install-dependencies.sh ├── README.md ├── License.txt ├── gradle.properties ├── flake.nix └── gradlew.bat /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/assets/branch.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /Branch-SDK/doc/package-list: -------------------------------------------------------------------------------- 1 | io.branch.referral 2 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/assets/pre_install_apps_null.branch: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Branch-SDK/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Branch-SDK/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Branch Android SDK 2 | POM_ARTIFACT_ID=library 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Branch-SDK-TestBed/signing.properties.sample: -------------------------------------------------------------------------------- 1 | STORE_FILE=/path/to/your.keystore 2 | STORE_PASSWORD=yourkeystorepass 3 | KEY_ALIAS=projectkeyalias 4 | KEY_PASSWORD=keyaliaspassword -------------------------------------------------------------------------------- /Branch-SDK/src/main/res/drawable/branch_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/Branch-SDK/src/main/res/drawable/branch_icon.png -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/Branch-SDK-TestBed/src/main/res/drawable/shadow.png -------------------------------------------------------------------------------- /Branch-SDK-TestBed/release/Branch-SDK-TestBed-release.aab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/Branch-SDK-TestBed/release/Branch-SDK-TestBed-release.aab -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/Branch-SDK-TestBed/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /Branch-SDK-TestBed/Branch-SDK-TestBed-release-unsigned.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BranchMetrics/android-branch-deep-linking-attribution/HEAD/Branch-SDK-TestBed/Branch-SDK-TestBed-release-unsigned.apk -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1001 4 | 1002 5 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | include(":Branch-SDK") 2 | include(":Branch-SDK-TestBed") 3 | 4 | pluginManagement { 5 | repositories { 6 | mavenLocal() 7 | gradlePluginPortal() 8 | google() 9 | mavenCentral() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/assets/pre_install_apps.branch: -------------------------------------------------------------------------------- 1 | { 2 | "apps": { 3 | "io.branch.referral.test": { 4 | "preinstall_partner": "branch_", 5 | "preinstall_campaign": "branch_campaign", 6 | "custom_key": "custom_value" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/menu/menu_log_output.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 06 12:51:09 PST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 5 | networkTimeout=10000 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/assets/pre_install_apps_no_package.branch: -------------------------------------------------------------------------------- 1 | { 2 | "apps": { 3 | "io.branch.referral.packager": { 4 | "preinstall_partner": "branch_", 5 | "preinstall_campaign": "branch_campaign", 6 | "custom_key": "custom_value" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/IBranchRequestTracingCallback.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import org.json.JSONObject; 4 | 5 | public interface IBranchRequestTracingCallback { 6 | void onRequestCompleted(String uri, JSONObject request, JSONObject response, String error, String requestUrl); 7 | } 8 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | If you discover a potential security issue in this project we ask that you notify Branch Security directly via email to security@branch.io 5 | Please do not open GitHub issues or pull requests - this makes the problem immediately visible to everyone, including malicious actors. 6 | -------------------------------------------------------------------------------- /Branch-SDK/proguard-consumer.txt: -------------------------------------------------------------------------------- 1 | # Remove references to Huawei's OAID (Google's advertising id equivalent) if the dependency is not available in your project. 2 | -dontwarn com.huawei.hms.ads.** 3 | 4 | -dontwarn com.miui.referrer.** 5 | -dontwarn com.samsung.android.sdk.sinstallreferrer.** 6 | 7 | -dontwarn com.google.android.gms.** 8 | 9 | -dontwarn com.android.billingclient.** 10 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/pill_button.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_first_page_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_last_page_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_person_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_insert_photo_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/AdType.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | /** 4 | * Ad Types 5 | */ 6 | public enum AdType { 7 | BANNER("BANNER"), 8 | INTERSTITIAL("INTERSTITIAL"), 9 | REWARDED_VIDEO("REWARDED_VIDEO"), 10 | NATIVE("NATIVE"); 11 | 12 | private final String name; 13 | 14 | AdType(String name) { 15 | this.name = name; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_logout_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_security_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_open_in_new_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/interfaces/IBranchLoggingCallbacks.java: -------------------------------------------------------------------------------- 1 | package io.branch.interfaces; 2 | 3 | /** 4 | * A set of callbacks that interface with the SDK's internal logging. 5 | */ 6 | public interface IBranchLoggingCallbacks { 7 | /** 8 | * Callback method that returns each time a log is generated 9 | * @param logMessage The log message 10 | * @param severityConstantName any of DEBUG, ERROR, INFO, WARN, VERBOSE 11 | */ 12 | void onBranchLog(String logMessage, String severityConstantName); 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: "📕 Documentation Issue" 5 | url: https://help.branch.io/developers-hub/docs/android-sdk-overview 6 | about: Report an issue in the Branch Android SDK Reference documentation by clicking "Suggest edits" button on the documentation page. 7 | - name: "Branch Support" 8 | url: https://help.branch.io/using-branch/page/submit-a-ticket 9 | about: If you are having general trouble with Branch Android SDK integration, please submit a ticket to Branch Support. 10 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/baseline_document_scanner_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | *.class 3 | Branch-SDK-TestBed/.settings/org.eclipse.jdt.core.prefs 4 | Branch-SDK/.settings/org.eclipse.jdt.core.prefs 5 | 6 | # Android Studio 7 | .idea 8 | *.iml 9 | local.properties 10 | build/ 11 | 12 | # Gradle 13 | .gradle/* 14 | .metadata/* 15 | 16 | *.gen 17 | bin/ 18 | gen/ 19 | 20 | # Mac stuff 21 | *.DS_Store 22 | 23 | # Branch 24 | Branch-SDK-TestBed/gradle/wrapper/gradle-wrapper.jar 25 | Branch-SDK-TestBed/gradlew 26 | Branch-SDK-TestBed/gradlew.bat 27 | 28 | Branch-SDK-TestBed/gradle/wrapper/gradle-wrapper.properties 29 | 30 | .direnv -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_doorbell_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_link_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_remove_red_eye_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Branch-SDK-TestBed 5 | 6 | install count = 7 | 8 | "Launched by Branch on auto deep linking!" 9 | "Launched by normal application flow" 10 | 11 | Disable ad network callouts 12 | Settings 13 | 14 | -------------------------------------------------------------------------------- /Branch-SDK/javadoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_emoji_events_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_shopping_bag_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/data/InstallReferrerResult.kt: -------------------------------------------------------------------------------- 1 | package io.branch.data 2 | 3 | data class InstallReferrerResult (var appStore: String?, 4 | var installBeginTimestampSeconds: Long, 5 | var installReferrer: String?, 6 | var referrerClickTimestampSeconds: Long, 7 | var installBeginTimestampServerSeconds: Long?, 8 | var referrerClickTimestampServerSeconds: Long?, 9 | var isClickThrough: Boolean = true) 10 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_person_off_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-20 15 | android.library=true 16 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_pageview_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-20 15 | android.library.reference.1=../Branch-SDK 16 | -------------------------------------------------------------------------------- /install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fix the CircleCI path 4 | export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH" 5 | 6 | DEPS="$ANDROID_HOME/installed-dependencies" 7 | 8 | if [ ! -e $DEPS ]; then 9 | cp -r /usr/local/android-sdk-linux $ANDROID_HOME && 10 | echo y | android update sdk -u -a -t android-23 && 11 | echo y | android update sdk -u -a -t platform-tools && 12 | echo y | android update sdk -u -a -t build-tools-21.1.2 && 13 | echo y | android update sdk -u -a -t sys-img-x86-android-23 && 14 | echo y | android update sdk -u -a -t addon-google_apis-google-18 && 15 | echo n | android create avd -n circleci-android23 -f -t android-23 --abi default/x86 && 16 | touch $DEPS 17 | fi 18 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/layout/activity_log_output.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_share_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/IntegrationValidatorCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import android.content.Context; 4 | 5 | public abstract class IntegrationValidatorCheck { 6 | String name; 7 | String errorMessage; 8 | String moreInfoLink; 9 | 10 | public abstract boolean RunTests(Context context); 11 | 12 | public String GetOutput(Context context, boolean didTestSucceed) { 13 | String symbol = RunTests(context) ? IntegrationValidatorConstants.checkmark : IntegrationValidatorConstants.xmark; 14 | return errorMessage; 15 | } 16 | 17 | public String GetTestName() { 18 | return name; 19 | } 20 | 21 | public String GetMoreInfoLink() { 22 | return moreInfoLink; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_monetization_on_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/layout/auto_deep_link_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/utils/AssetUtils.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.utils; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | 9 | /** 10 | * Asset Utilities. 11 | */ 12 | public class AssetUtils { 13 | public static String readJsonFile (Context context, String filename) { 14 | StringBuilder sb = new StringBuilder(); 15 | try { 16 | BufferedReader br = new BufferedReader(new InputStreamReader(context.getAssets().open(filename))); 17 | String line = br.readLine(); 18 | while (line != null) { 19 | sb.append(line); 20 | line = br.readLine(); 21 | } 22 | } catch (IOException e) { 23 | e.printStackTrace(); 24 | } 25 | return sb.toString(); 26 | } 27 | } -------------------------------------------------------------------------------- /Branch-SDK/doc/script.js: -------------------------------------------------------------------------------- 1 | function show(type) 2 | { 3 | count = 0; 4 | for (var key in methods) { 5 | var row = document.getElementById(key); 6 | if ((methods[key] & type) != 0) { 7 | row.style.display = ''; 8 | row.className = (count++ % 2) ? rowColor : altColor; 9 | } 10 | else 11 | row.style.display = 'none'; 12 | } 13 | updateTabs(type); 14 | } 15 | 16 | function updateTabs(type) 17 | { 18 | for (var value in tabs) { 19 | var sNode = document.getElementById(tabs[value][0]); 20 | var spanNode = sNode.firstChild; 21 | if (value == type) { 22 | sNode.className = activeTableTab; 23 | spanNode.innerHTML = tabs[value][1]; 24 | } 25 | else { 26 | sNode.className = tableTab; 27 | spanNode.innerHTML = "" + tabs[value][1] + ""; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchApp.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | *

7 | * Default Android Application class for Branch SDK. You should use this as your application class 8 | * in your manifest if you are not creating an Application class. If you already have an Application 9 | * class then you can either extend your Application class with BranchApp or initialize Branch yourself 10 | * via Branch.getAutoInstance(this);. 11 | *

12 | *

13 | * Add this entry to the manifest if you don't have an Application class : 14 | *

15 | *
16 |  *      <application
17 |  *      -----
18 |  *      android:name="io.branch.referral.BranchApp">
19 |  *
20 | */ 21 | public class BranchApp extends Application { 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | Branch.getAutoInstance(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Branch-SDK/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | 22 | # Huawei's OAID (Google's advertising id equivalent) 23 | -keep class com.huawei.hms.ads.** { *; } 24 | -keep interface com.huawei.hms.ads.** { *; } -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Reference 2 | SDK-XXX -- . 3 | 4 | ## Description 5 | <!-- SUFFICIENT DESCRIPTION TO EXPLAIN THE PROBLEM THAT IS BEING SOLVED --> 6 | 7 | ## Testing Instructions 8 | <!-- TESTING INSTRUCTIONS --> 9 | 10 | ## Risk Assessment [`HIGH` || `MEDIUM` || `LOW`] 11 | <!-- CHOOSE ONE OF THE THREE ASSESSMENTS ABOVE --> 12 | <!-- FOR MEDIUM OR HIGH ASSESSMENTS, ADD ADDITIONAL NOTES HERE --> 13 | 14 | - [ ] I, the PR creator, have tested — integration, unit, or otherwise — this code. 15 | 16 | ## Reviewer Checklist (To be checked off by the reviewer only) 17 | 18 | - [ ] JIRA Ticket is referenced in PR title. 19 | - Correctness & Style 20 | - [ ] Conforms to [AOSP Style Guides](https://source.android.com/setup/contribute/code-style) 21 | - [ ] Mission critical pieces are documented in code and out of code as needed. 22 | - [ ] Unit Tests reviewed and test issue sufficiently. 23 | - [ ] Functionality was reviewed in QA independently by another engineer on the team. 24 | 25 | cc @BranchMetrics/saas-sdk-devs for visibility. 26 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchAsyncTask.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.os.AsyncTask; 4 | import android.os.Build; 5 | 6 | /** 7 | * <p> 8 | * Convenient class for handling ASync task with pool executor depending on the SDK platform 9 | * </p> 10 | */ 11 | public abstract class BranchAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 12 | 13 | /** 14 | * Execute Params in back ground depending on the platform version. This executes task in parallel with the {@link AsyncTask#THREAD_POOL_EXECUTOR} 15 | * 16 | * @param params Params for executing this Async task 17 | * @return This object for method chaining 18 | */ 19 | public final AsyncTask<Params, Progress, Result> executeTask(Params... params) { 20 | try { 21 | return executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 22 | } catch (Exception t) { 23 | BranchLogger.w("Caught Exception in AsyncTask: " + t.getMessage()); 24 | return execute(params); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/receivers/SharingBroadcastReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.branch.receivers 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.ComponentName 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.Intent.EXTRA_CHOSEN_COMPONENT 8 | import io.branch.referral.BranchLogger 9 | import io.branch.referral.NativeShareLinkManager 10 | import io.branch.referral.util.SharingUtil 11 | 12 | class SharingBroadcastReceiver: BroadcastReceiver() { 13 | override fun onReceive(context: Context, intent: Intent) { 14 | val clickedComponent: ComponentName? = intent.getParcelableExtra(EXTRA_CHOSEN_COMPONENT); 15 | 16 | BranchLogger.v("Intent: $intent") 17 | BranchLogger.v("Clicked component: $clickedComponent") 18 | 19 | NativeShareLinkManager.getInstance().linkShareListenerCallback?.onChannelSelected( 20 | clickedComponent.toString() 21 | ) 22 | 23 | NativeShareLinkManager.getInstance().linkShareListenerCallback?.onLinkShareResponse(SharingUtil.sharedURL, null); 24 | } 25 | } -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/KeyTest.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(AndroidJUnit4.class) 10 | public class KeyTest extends BranchTest { 11 | 12 | @Test 13 | public void testManifestKeys() { 14 | Assert.assertFalse(BranchUtil.isTestModeEnabled()); 15 | 16 | String branchKey = BranchUtil.readBranchKey(getTestContext()); 17 | Assert.assertTrue(branchKey.startsWith("key_live")); 18 | 19 | Branch.enableTestMode(); 20 | branchKey = BranchUtil.readBranchKey(getTestContext()); 21 | Assert.assertTrue(branchKey.startsWith("key_test")); 22 | } 23 | 24 | @Test 25 | public void testAutoInstanceWithKey() { 26 | final String expectedKey = "key_XXX"; 27 | initBranchInstance(expectedKey); 28 | 29 | final String actualKey = PrefHelper.getInstance(getTestContext()).getBranchKey(); 30 | Assert.assertEquals(expectedKey, actualKey); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Branch SDK Documentation for Android 2 | 3 | The Branch Android SDK for deep linking and attribution. 4 | 5 | > ## Branch Sandbox Program 6 | > 7 | > As part of this [sandbox program](https://help.branch.io/developers-hub/docs/branch-sandbox-program), you will be able to partner directly with Branch’s Product and Engineering team for exclusive access to test drive our innovative products before the market and proactively exchange valuable feedback. With your support, we will create the winning measurement and linking solutions of the future together. 8 | > 9 | > **If you would like to join, please reach out to us today at [sandbox@branch.io](mailto:sandbox@branch.io "mailto:sandbox@branch.io") or by filling out this [form](https://branch.link/sandbox?~channel=android-repo).** 10 | 11 | Branch helps mobile apps grow with deep links / deeplinks that power paid acquisition and re-engagement campaigns, referral programs, content sharing, deep linked emails, smart banners, custom user onboarding, and more. 12 | 13 | View [Branch's SDK documentation for Android](https://help.branch.io/developers-hub/docs/android-sdk-overview) 14 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Branch Metrics, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # This workflow warns and then closes issues that have had no activity for a specified amount of time. 2 | # 3 | # You can adjust the behavior by modifying this file. 4 | # For more information, see: 5 | # https://github.com/actions/stale 6 | name: Mark stale issues 7 | 8 | on: 9 | schedule: 10 | - cron: '0 0 * * *' 11 | 12 | jobs: 13 | stale: 14 | 15 | runs-on: ubuntu-latest 16 | permissions: 17 | issues: write 18 | 19 | steps: 20 | - uses: actions/stale@v10 21 | with: 22 | days-before-issue-stale: 60 23 | days-before-close: 7 24 | stale-issue-message: 'This issue has been automatically marked as stale due to inactivity for 60 days. If this issue is still relevant, please respond with any updates or this issue will be closed in 7 days. If you believe this is a mistake, please comment to let us know. Thank you for your contributions.' 25 | stale-issue-label: 'no-issue-activity' 26 | close-issue-message: 'This issue has been closed due to inactivity. If this issue is still relevant, please reopen it or create a new one. Thank you for your contributions.' 27 | start-date: '2023-05-22' 28 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | <vector android:height="24dp" android:tint="#FFFFFF" 2 | android:viewportHeight="24" android:viewportWidth="24" 3 | android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> 4 | <path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/> 5 | </vector> 6 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/BranchKeysValidatorCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.branchKeysMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | import android.text.TextUtils; 7 | 8 | import io.branch.referral.BranchUtil; 9 | 10 | public class BranchKeysValidatorCheck extends IntegrationValidatorCheck { 11 | 12 | String name = "Branch Keys"; 13 | String errorMessage = "Unable to read Branch keys from your application. Did you forget to add Branch keys in your application?."; 14 | String moreInfoLink = branchKeysMoreInfoDocsLink; 15 | 16 | public BranchKeysValidatorCheck() { 17 | super.name = name; 18 | super.errorMessage = errorMessage; 19 | super.moreInfoLink = moreInfoLink; 20 | } 21 | 22 | @Override 23 | public boolean RunTests(Context context) { 24 | return !TextUtils.isEmpty(BranchUtil.readBranchKey(context)); 25 | } 26 | 27 | @Override 28 | public String GetOutput(Context context, boolean didTestSucceed) { 29 | didTestSucceed = RunTests(context); 30 | return super.GetOutput(context, didTestSucceed); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/LinkingValidator.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import android.content.Context; 4 | import android.view.WindowManager; 5 | import android.widget.Button; 6 | 7 | import java.util.Objects; 8 | 9 | public class LinkingValidator { 10 | private static LinkingValidator instance; 11 | private LinkingValidatorDialog linkingValidatorDialog; 12 | 13 | private LinkingValidator(Context context) {} 14 | 15 | public static void validate(Context context) { 16 | if (instance == null) { 17 | instance = new LinkingValidator(context); 18 | } 19 | instance.linkingValidatorDialog = new LinkingValidatorDialog(context); 20 | instance.validateDeepLinkRouting(context); 21 | } 22 | 23 | private void validateDeepLinkRouting(Context context) { 24 | WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 25 | lp.copyFrom(Objects.requireNonNull(instance.linkingValidatorDialog.getWindow()).getAttributes()); 26 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; 27 | lp.height = 2000; 28 | instance.linkingValidatorDialog.show(); 29 | instance.linkingValidatorDialog.getWindow().setAttributes(lp); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/DependencyUtils.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util 2 | 3 | import io.branch.referral.BranchLogger 4 | 5 | fun classExists(className: String): Boolean { 6 | return try { 7 | Class.forName(className) 8 | true 9 | } catch (e: ClassNotFoundException) { 10 | BranchLogger.v("Could not find $className. If expected, import the dependency into your app.") 11 | false 12 | } 13 | } 14 | 15 | const val playStoreInstallReferrerClass = "com.android.installreferrer.api.InstallReferrerClient" 16 | 17 | const val playStoreAdvertisingIdClientClass = 18 | "com.google.android.gms.ads.identifier.AdvertisingIdClient" 19 | 20 | const val huaweiAdvertisingIdClientClass = "com.huawei.hms.ads.identifier.AdvertisingIdClient"; 21 | 22 | const val huaweiInstallReferrerClass = 23 | "com.huawei.hms.ads.installreferrer.api.InstallReferrerClient" 24 | 25 | const val samsungInstallReferrerClass = 26 | "com.samsung.android.sdk.sinstallreferrer.api.InstallReferrerClient" 27 | 28 | const val xiaomiInstallReferrerClass = "com.miui.referrer.api.GetAppsReferrerClient" 29 | 30 | const val billingGooglePlayClass = "com.android.billingclient.api.BillingClient" 31 | 32 | const val androidBrowserClass = "androidx.browser.customtabs.CustomTabsIntent" -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=5.20.3 2 | VERSION_CODE=052403 3 | GROUP=io.branch.sdk.android 4 | 5 | POM_DESCRIPTION=Use the Branch SDK (branch.io) to create and power the links that point back to your apps for all of these things and more. Branch makes it incredibly simple to create powerful deep links that can pass data across app install and open while handling all edge cases (using on desktop vs. mobile vs. already having the app installed, etc). Best of all, it is really simple to start using the links for your own app: only 2 lines of code to register the deep link router and one more line of code to create the links with custom data. 6 | POM_URL=https://github.com/BranchMetrics/Branch-Android-SDK/ 7 | POM_SCM_URL=https://github.com/BranchMetrics/Branch-Android-SDK/ 8 | POM_SCM_CONNECTION=scm:git@github.com:BranchMetrics/Branch-Android-SDK.git 9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:BranchMetrics/Branch-Android-SDK.git 10 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 11 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 12 | POM_LICENCE_DIST=repo 13 | POM_DEVELOPER_ID=branch 14 | POM_DEVELOPER_NAME=Branch Metrics 15 | 16 | ANDROID_BUILD_SDK_VERSION_MINIMUM=21 17 | ANDROID_BUILD_SDK_VERSION_COMPILE=34 18 | ANDROID_BUILD_TOOLS_VERSION=34.0.0 19 | android.useAndroidX=true 20 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/drawable/ic_baseline_qr_code_24.xml: -------------------------------------------------------------------------------- 1 | <vector android:height="24dp" android:tint="#FFFFFF" 2 | android:viewportHeight="24" android:viewportWidth="24" 3 | android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> 4 | <path android:fillColor="@android:color/white" android:pathData="M3,11h8V3H3V11zM5,5h4v4H5V5z"/> 5 | <path android:fillColor="@android:color/white" android:pathData="M3,21h8v-8H3V21zM5,15h4v4H5V15z"/> 6 | <path android:fillColor="@android:color/white" android:pathData="M13,3v8h8V3H13zM19,9h-4V5h4V9z"/> 7 | <path android:fillColor="@android:color/white" android:pathData="M19,19h2v2h-2z"/> 8 | <path android:fillColor="@android:color/white" android:pathData="M13,13h2v2h-2z"/> 9 | <path android:fillColor="@android:color/white" android:pathData="M15,15h2v2h-2z"/> 10 | <path android:fillColor="@android:color/white" android:pathData="M13,17h2v2h-2z"/> 11 | <path android:fillColor="@android:color/white" android:pathData="M15,19h2v2h-2z"/> 12 | <path android:fillColor="@android:color/white" android:pathData="M17,17h2v2h-2z"/> 13 | <path android:fillColor="@android:color/white" android:pathData="M17,13h2v2h-2z"/> 14 | <path android:fillColor="@android:color/white" android:pathData="M19,15h2v2h-2z"/> 15 | </vector> 16 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/SharingHelper.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | /** 4 | * Define the Applications for the sharing the link with. 5 | */ 6 | public class SharingHelper { 7 | /** 8 | * <p> 9 | * Defines the Application for sharing a deep link with. 10 | * </p> 11 | */ 12 | public enum SHARE_WITH { 13 | FACEBOOK("com.facebook.katana"), 14 | FACEBOOK_MESSENGER("com.facebook.orca"), 15 | TWITTER("com.twitter.android"), 16 | MESSAGE(".mms"), 17 | EMAIL("com.google.android.email"), 18 | FLICKR("com.yahoo.mobile.client.android.flickr"), 19 | GOOGLE_DOC("com.google.android.apps.docs"), 20 | WHATS_APP("com.whatsapp"), 21 | PINTEREST("com.pinterest"), 22 | HANGOUT("com.google.android.talk"), 23 | INSTAGRAM("com.instagram.android"), 24 | WECHAT("jom.tencent.mm"), 25 | SNAPCHAT("com.snapchat.android"), 26 | GMAIL("com.google.android.gm"); 27 | 28 | private String name = ""; 29 | 30 | private SHARE_WITH(String key) { 31 | this.name = key; 32 | } 33 | 34 | public String getAppName() { 35 | return name; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return name; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/BranchInstanceCreationValidatorCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.branchInstanceCreationMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | 7 | import io.branch.referral.Branch; 8 | 9 | public class BranchInstanceCreationValidatorCheck extends IntegrationValidatorCheck { 10 | 11 | String name = "Branch instance"; 12 | String errorMessage = "Branch is not initialised from your Application class. Please add `Branch.getAutoInstance(this);` to your Application#onCreate() method."; 13 | String moreInfoLink = branchInstanceCreationMoreInfoDocsLink; 14 | 15 | public BranchInstanceCreationValidatorCheck() { 16 | super.name = name; 17 | super.errorMessage = errorMessage; 18 | super.moreInfoLink = moreInfoLink; 19 | } 20 | 21 | @Override 22 | public boolean RunTests(Context context) { 23 | return Branch.getInstance() != null; 24 | } 25 | 26 | @Override 27 | public String GetOutput(Context context, boolean didTestSucceed) { 28 | didTestSucceed = RunTests(context); 29 | return super.GetOutput(context, didTestSucceed); 30 | } 31 | 32 | @Override 33 | public String GetMoreInfoLink() { 34 | return moreInfoLink; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/BranchContentSchema.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | import android.text.TextUtils; 4 | 5 | /** 6 | * Created by sojanpr on 7/7/17. 7 | * Enumerations for BranchContentSchema. This enumearates all content schemas supported by Branch 8 | */ 9 | public enum BranchContentSchema { 10 | COMMERCE_AUCTION, 11 | COMMERCE_BUSINESS, 12 | COMMERCE_OTHER, 13 | COMMERCE_PRODUCT, 14 | COMMERCE_RESTAURANT, 15 | COMMERCE_SERVICE, 16 | COMMERCE_TRAVEL_FLIGHT, 17 | COMMERCE_TRAVEL_HOTEL, 18 | COMMERCE_TRAVEL_OTHER, 19 | GAME_STATE, 20 | MEDIA_IMAGE, 21 | MEDIA_MIXED, 22 | MEDIA_MUSIC, 23 | MEDIA_OTHER, 24 | MEDIA_VIDEO, 25 | OTHER, 26 | TEXT_ARTICLE, 27 | TEXT_BLOG, 28 | TEXT_OTHER, 29 | TEXT_RECIPE, 30 | TEXT_REVIEW, 31 | TEXT_SEARCH_RESULTS, 32 | TEXT_STORY, 33 | TEXT_TECHNICAL_DOC; 34 | 35 | public static BranchContentSchema getValue(String name) { 36 | BranchContentSchema schema = null; 37 | if (!TextUtils.isEmpty(name)) { 38 | for (BranchContentSchema contentSchema : BranchContentSchema.values()) { 39 | if (contentSchema.name().equalsIgnoreCase(name)) { 40 | schema = contentSchema; 41 | break; 42 | } 43 | } 44 | } 45 | return schema; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/test/mock/MockActivity.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.test.mock; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | import org.json.JSONObject; 9 | import org.junit.Assert; 10 | 11 | import java.util.concurrent.CountDownLatch; 12 | 13 | import io.branch.referral.Branch; 14 | import io.branch.referral.BranchError; 15 | import io.branch.referral.PrefHelper; 16 | 17 | public class MockActivity extends Activity { 18 | private static final String TAG = "MockActivity"; 19 | public String state; 20 | public boolean initSessionCallbackInvoked = false; 21 | 22 | @Override 23 | public void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | state = "created"; 26 | } 27 | 28 | @Override 29 | public void onStart() { 30 | super.onStart(); 31 | state = "started"; 32 | } 33 | 34 | @Override 35 | public void onResume() { 36 | super.onResume(); 37 | state = "resumed"; 38 | } 39 | 40 | @Override 41 | public void onPause() { 42 | super.onPause(); 43 | state = "paused"; 44 | } 45 | 46 | @Override 47 | public void onStop() { 48 | super.onStop(); 49 | state = "stopped"; 50 | } 51 | 52 | @Override 53 | public void onDestroy() { 54 | super.onDestroy(); 55 | state = "destroyed"; 56 | } 57 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchUniversalReferralInitWrapper.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import org.json.JSONObject; 4 | 5 | import io.branch.indexing.BranchUniversalObject; 6 | import io.branch.referral.util.LinkProperties; 7 | 8 | /** 9 | * Class for converting init session callback with {@link Branch.BranchReferralInitListener} to {@link Branch.BranchUniversalReferralInitListener} 10 | */ 11 | class BranchUniversalReferralInitWrapper implements Branch.BranchReferralInitListener { 12 | private final Branch.BranchUniversalReferralInitListener universalReferralInitListener_; 13 | 14 | public BranchUniversalReferralInitWrapper(Branch.BranchUniversalReferralInitListener universalReferralInitListener) { 15 | this.universalReferralInitListener_ = universalReferralInitListener; 16 | } 17 | 18 | @Override 19 | public void onInitFinished(JSONObject referringParams, BranchError error) { 20 | if (universalReferralInitListener_ != null) { 21 | if (error != null) { 22 | universalReferralInitListener_.onInitFinished(null, null, error); 23 | } else { 24 | BranchUniversalObject branchUniversalObject = BranchUniversalObject.getReferredBranchUniversalObject(); 25 | LinkProperties linkProperties = LinkProperties.getReferredLinkProperties(); 26 | universalReferralInitListener_.onInitFinished(branchUniversalObject, linkProperties, error); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/BRANCH_STANDARD_EVENT.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | /** 4 | * Created by sojanpr on 7/5/17. 5 | * <p> 6 | * Standard Branch Events enumeration. These events are used with Branch standard events 7 | * </p> 8 | */ 9 | public enum BRANCH_STANDARD_EVENT { 10 | // Commerce events 11 | ADD_TO_CART("ADD_TO_CART"), 12 | ADD_TO_WISHLIST("ADD_TO_WISHLIST"), 13 | VIEW_CART("VIEW_CART"), 14 | INITIATE_PURCHASE("INITIATE_PURCHASE"), 15 | ADD_PAYMENT_INFO("ADD_PAYMENT_INFO"), 16 | PURCHASE("PURCHASE"), 17 | SPEND_CREDITS("SPEND_CREDITS"), 18 | // Content events 19 | SEARCH("SEARCH"), 20 | VIEW_ITEM("VIEW_ITEM"), 21 | VIEW_ITEMS("VIEW_ITEMS"), 22 | RATE("RATE"), 23 | SHARE("SHARE"), 24 | INITIATE_STREAM("INITIATE_STREAM"), 25 | COMPLETE_STREAM("COMPLETE_STREAM"), 26 | 27 | // User lifecycle events 28 | COMPLETE_REGISTRATION("COMPLETE_REGISTRATION"), 29 | COMPLETE_TUTORIAL("COMPLETE_TUTORIAL"), 30 | ACHIEVE_LEVEL("ACHIEVE_LEVEL"), 31 | UNLOCK_ACHIEVEMENT("UNLOCK_ACHIEVEMENT"), 32 | 33 | // V2 Events 34 | INVITE("INVITE"), 35 | LOGIN("LOGIN"), 36 | RESERVE("RESERVE"), 37 | SUBSCRIBE("SUBSCRIBE"), 38 | START_TRIAL("START_TRIAL"), 39 | CLICK_AD("CLICK_AD"), 40 | VIEW_AD("VIEW_AD"); 41 | 42 | private final String name; 43 | 44 | BRANCH_STANDARD_EVENT(String name) { 45 | this.name = name; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/AutoDeepLinkTestActivity.java: -------------------------------------------------------------------------------- 1 | package io.branch.branchandroidtestbed; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | import android.widget.TextView; 6 | 7 | import io.branch.referral.Branch; 8 | 9 | /** 10 | * Created by sojanpr on 7/21/15. 11 | * <p> Activity to demonstrate the auto deep linking functionality. 12 | * This activity is defined in the manifest with Keys for auto deep linking. 13 | * See manifest file for auto deep link configurations.</p> 14 | */ 15 | public class AutoDeepLinkTestActivity extends Activity { 16 | 17 | @Override 18 | protected void onResume() { 19 | super.onResume(); 20 | setContentView(R.layout.auto_deep_link_test); 21 | 22 | TextView launch_mode_txt = findViewById(R.id.launch_mode_txt); 23 | if (Branch.isAutoDeepLinkLaunch(this)) { 24 | launch_mode_txt.setText(R.string.launch_mode_branch); 25 | Branch.getInstance().getLatestReferringParams(); 26 | } else { 27 | launch_mode_txt.setText(R.string.launch_mode_normal); 28 | } 29 | 30 | //You can also get linked params for the intent extra. 31 | if (getIntent().getExtras() != null && getIntent().getExtras().keySet() != null) { 32 | for (String key : getIntent().getExtras().keySet()) { 33 | Log.i("BranchTestBed:", "Deep Linked Param " + 34 | key + " = " + getIntent().getExtras().getString(key)); 35 | } 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/PackageNameCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.packageNameMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | 7 | import org.json.JSONObject; 8 | 9 | public class PackageNameCheck extends IntegrationValidatorCheck { 10 | 11 | String name = "Package Name"; 12 | String errorMessage = "Incorrect package name in Branch dashboard. Please correct your package name in dashboard -> Configuration page."; 13 | String moreInfoLink = packageNameMoreInfoDocsLink; 14 | BranchIntegrationModel integrationModel; 15 | JSONObject branchAppConfig; 16 | 17 | public PackageNameCheck(BranchIntegrationModel integrationModel, JSONObject branchAppConfig) { 18 | super.name = name; 19 | super.errorMessage = errorMessage; 20 | super.moreInfoLink = moreInfoLink; 21 | this.integrationModel = integrationModel; 22 | this.branchAppConfig = branchAppConfig; 23 | } 24 | 25 | @Override 26 | public boolean RunTests(Context context) { 27 | String valueOnDashboard = integrationModel.packageName; 28 | String valueInManifest = branchAppConfig.optString("android_package_name"); 29 | return valueOnDashboard.equals(valueInManifest); 30 | } 31 | 32 | @Override 33 | public String GetOutput(Context context, boolean didTestSucceed) { 34 | didTestSucceed = RunTests(context); 35 | return super.GetOutput(context, didTestSucceed); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/SystemObserverTests.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral 2 | 3 | import android.util.Log 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | import org.junit.Assert 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | import java.util.UUID 9 | 10 | @RunWith(AndroidJUnit4::class) 11 | class SystemObserverTests : BranchTest() { 12 | @Test 13 | fun testAnonID() { 14 | initBranchInstance() 15 | val anonID = SystemObserver.getAnonID(testContext) 16 | try { 17 | UUID.fromString(anonID) 18 | } catch (e: Exception) { 19 | Assert.fail() 20 | } 21 | } 22 | 23 | @Test 24 | fun testAnonIDChangesWithDisableTracking() { 25 | // TODO: figure out how to handle disable tracking, seems the tracking controller is not very testable 26 | } 27 | 28 | @Test 29 | fun fetchAdIdLatNotEnabledGaidNotNull(){ 30 | initBranchInstance() 31 | val so = DeviceInfo.getInstance().systemObserver 32 | 33 | so.fetchAdId( 34 | testContext 35 | ) { 36 | if(so.latVal == 0){ 37 | Assert.assertNotNull(so.aid) 38 | } 39 | } 40 | } 41 | 42 | @Test 43 | fun fetchAdIdLatEnabledAidNull(){ 44 | initBranchInstance() 45 | val so = DeviceInfo.getInstance().systemObserver 46 | 47 | so.fetchAdId( 48 | testContext 49 | ) { 50 | if(so.latVal == 1){ 51 | Assert.assertNull(so.aid) 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/SharingUtil.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util 2 | 3 | import android.app.Activity 4 | import android.app.PendingIntent 5 | import android.content.Intent 6 | import android.os.Build 7 | import androidx.annotation.RequiresApi 8 | import io.branch.receivers.SharingBroadcastReceiver 9 | 10 | object SharingUtil { 11 | var sharedURL: String? = "" 12 | 13 | @JvmStatic @RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1) 14 | fun share(text: String, title: String?, subject: String?, activity: Activity) { 15 | sharedURL = text 16 | val immutabilityIntentFlags: Int = 17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 18 | PendingIntent.FLAG_MUTABLE 19 | } else { 20 | 0 21 | } 22 | val shareIntent = Intent().apply { 23 | action = Intent.ACTION_SEND 24 | type = "text/plain" 25 | putExtra(Intent.EXTRA_TEXT, text) 26 | putExtra(Intent.EXTRA_TITLE, title) 27 | putExtra(Intent.EXTRA_SUBJECT, subject) 28 | } 29 | val chooserIntent = 30 | Intent.createChooser( 31 | shareIntent, 32 | title, 33 | PendingIntent.getBroadcast( 34 | activity.applicationContext, 35 | 0, 36 | Intent(activity.applicationContext, SharingBroadcastReceiver::class.java), 37 | PendingIntent.FLAG_UPDATE_CURRENT or immutabilityIntentFlags 38 | ).intentSender 39 | ) 40 | 41 | activity.startActivityForResult(chooserIntent, 1002) 42 | } 43 | } -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | <resources> 2 | <style name="Share_Sheet_Style" parent="android:style/Widget.ListView"> 3 | <item name="android:divider">@android:color/holo_red_dark</item> 4 | <item name="android:dividerHeight">1dp</item> 5 | <item name="android:listSelector">@android:drawable/ic_menu_share</item> 6 | <item name="android:drawSelectorOnTop">false</item> 7 | <item name="android:cacheColorHint">#FF00FF00</item> 8 | </style> 9 | 10 | <style name="testbed_button_style"> 11 | <item name="android:layout_width">320dp</item> 12 | <item name="android:layout_height">60dp</item> 13 | <item name="android:layout_marginTop">10dp</item> 14 | <item name="android:textAppearance">@android:style/TextAppearance.Medium</item> 15 | <item name="android:textColor">@android:color/white</item> 16 | <item name="android:background">@drawable/pill_button</item> 17 | <item name="android:paddingStart">10dp</item> 18 | <item name="android:drawablePadding">10dp</item> 19 | <item name="android:gravity">left|center_vertical</item> 20 | 21 | </style> 22 | 23 | <style name="testbed_text_view"> 24 | <item name="android:layout_width">360dp</item> 25 | <item name="android:layout_height">50dp</item> 26 | <item name="android:textColor">#0074DF</item> 27 | <item name="android:gravity">center_vertical</item> 28 | <item name="android:textSize">18dp</item> 29 | </style> 30 | 31 | <style name="AppTheme" parent=""> 32 | <item name="android:statusBarColor">#0074DF</item> 33 | <item name="android:colorPrimary">#0074DF</item> 34 | </style> 35 | 36 | </resources> 37 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/res/layout/linking_validator_dialog_row_item.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 | android:orientation="vertical" 4 | android:layout_width="match_parent" 5 | android:layout_height="match_parent"> 6 | 7 | <LinearLayout 8 | android:layout_width="match_parent" 9 | android:layout_height="50dp" 10 | android:orientation="horizontal"> 11 | 12 | <TextView 13 | android:id="@+id/linkingValidatorRowTitleText" 14 | android:layout_width="0dp" 15 | android:layout_height="wrap_content" 16 | android:layout_weight="0.4" 17 | android:text="Title" 18 | android:textSize="14sp" /> 19 | 20 | <Button 21 | android:id="@+id/linkingValidatorRowInfoButton" 22 | android:layout_width="0dp" 23 | android:layout_height="match_parent" 24 | android:layout_weight="0.2" 25 | android:text="Info" 26 | android:textSize="9sp" /> 27 | 28 | <Button 29 | android:id="@+id/linkingValidatorRowActionButton" 30 | android:layout_width="0dp" 31 | android:layout_height="match_parent" 32 | android:layout_weight="0.2" 33 | android:text="Share" 34 | android:textSize="9sp" /> 35 | 36 | <Button 37 | android:id="@+id/linkingValidatorRowDebugButton" 38 | android:layout_width="0dp" 39 | android:layout_height="match_parent" 40 | android:layout_weight="0.2" 41 | android:text="Debug" 42 | android:textSize="9sp" /> 43 | 44 | </LinearLayout> 45 | 46 | </LinearLayout> -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/ServerRequestGetAppConfig.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import android.content.Context; 4 | 5 | import org.json.JSONObject; 6 | 7 | import io.branch.referral.Branch; 8 | import io.branch.referral.Defines; 9 | import io.branch.referral.ServerRequest; 10 | import io.branch.referral.ServerResponse; 11 | 12 | class ServerRequestGetAppConfig extends ServerRequest { 13 | private final IGetAppConfigEvents callback; 14 | 15 | public ServerRequestGetAppConfig(Context context, IGetAppConfigEvents callback) { 16 | super(context, Defines.RequestPath.GetApp); 17 | this.callback = callback; 18 | } 19 | 20 | @Override 21 | public boolean handleErrors(Context context) { 22 | return false; 23 | } 24 | 25 | @Override 26 | public void onRequestSucceeded(ServerResponse response, Branch branch) { 27 | if (callback != null) { 28 | callback.onAppConfigAvailable(response.getObject()); 29 | } 30 | } 31 | 32 | @Override 33 | public void handleFailure(int statusCode, String causeMsg) { 34 | if (callback != null) { 35 | callback.onAppConfigAvailable(null); 36 | } 37 | } 38 | 39 | @Override 40 | public boolean isGetRequest() { 41 | return true; 42 | } 43 | 44 | @Override 45 | public String getRequestUrl() { 46 | // Always use default URL for app config validation endpoint for security reasons 47 | return prefHelper_.getAPIBaseUrl(false) + getRequestPath() + "/" + prefHelper_.getBranchKey(); 48 | } 49 | 50 | @Override 51 | public void clearCallbacks() { 52 | 53 | } 54 | 55 | public interface IGetAppConfigEvents { 56 | void onAppConfigAvailable(JSONObject obj); 57 | } 58 | } -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/DebugTest.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(AndroidJUnit4.class) 10 | public class DebugTest extends BranchTest { 11 | 12 | @Test 13 | public void testTestModeA() { 14 | // Test Mode should be off to start 15 | Assert.assertFalse(BranchUtil.isTestModeEnabled()); 16 | 17 | // Test Mode should still be off after this check (check has a side effect) 18 | Assert.assertFalse(BranchUtil.checkTestMode(getTestContext())); 19 | 20 | // Enable Test Mode after check 21 | Branch.enableTestMode(); 22 | Assert.assertTrue(BranchUtil.isTestModeEnabled()); 23 | 24 | // Assert that checking for test mode is also now true 25 | Assert.assertTrue(BranchUtil.checkTestMode(getTestContext())); 26 | } 27 | 28 | @Test 29 | public void testTestModeB() { 30 | // Test Mode should be off to start 31 | Assert.assertFalse(BranchUtil.isTestModeEnabled()); 32 | 33 | // Enable Test Mode before check 34 | Branch.enableTestMode(); 35 | Assert.assertTrue(BranchUtil.isTestModeEnabled()); 36 | 37 | // Test Mode should still be on after this check (check has a side effect) 38 | Assert.assertTrue(BranchUtil.checkTestMode(getTestContext())); 39 | 40 | // Assert that this test is also still true 41 | Assert.assertTrue(BranchUtil.isTestModeEnabled()); 42 | 43 | // Now turn off test mode and check 44 | Branch.disableTestMode(); 45 | 46 | Assert.assertFalse(BranchUtil.isTestModeEnabled()); 47 | Assert.assertFalse(BranchUtil.checkTestMode(getTestContext())); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/coroutines/AdvertisingIds.kt: -------------------------------------------------------------------------------- 1 | package io.branch.coroutines 2 | 3 | import android.content.Context 4 | import android.provider.Settings 5 | import com.google.android.gms.ads.identifier.AdvertisingIdClient 6 | import io.branch.referral.BranchLogger 7 | import io.branch.referral.PrefHelper 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.withContext 10 | 11 | suspend fun getGoogleAdvertisingInfoObject(context: Context): AdvertisingIdClient.Info? { 12 | return withContext(Dispatchers.Default) { 13 | try { 14 | AdvertisingIdClient.getAdvertisingIdInfo(context) 15 | } 16 | catch (exception: Exception) { 17 | BranchLogger.w("Caught getGoogleAdvertisingInfoObject exception: $exception") 18 | null 19 | } 20 | } 21 | } 22 | 23 | suspend fun getHuaweiAdvertisingInfoObject(context: Context): com.huawei.hms.ads.identifier.AdvertisingIdClient.Info? { 24 | return withContext(Dispatchers.Default) { 25 | try { 26 | com.huawei.hms.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo(context) 27 | } 28 | catch (exception: Exception) { 29 | BranchLogger.w("Caught getHuaweiAdvertisingInfoObject exception: $exception") 30 | null 31 | } 32 | } 33 | } 34 | 35 | suspend fun getAmazonFireAdvertisingInfoObject(context: Context): Pair<Int, String>? { 36 | return withContext(Dispatchers.Default) { 37 | try { 38 | val cr = context.contentResolver 39 | Pair( 40 | Settings.Secure.getInt(cr, "limit_ad_tracking"), 41 | Settings.Secure.getString(cr, "advertising_id") 42 | ) 43 | } 44 | catch (exception: Exception) { 45 | BranchLogger.w("Caught getAmazonFireAdvertisingInfo exception: $exception") 46 | null 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/res/layout/integration_validator_dialog_row_item.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 | android:layout_width="match_parent" 4 | android:layout_height="wrap_content" 5 | android:orientation="horizontal" 6 | android:weightSum="1" 7 | android:layout_gravity="left" 8 | android:gravity="left"> 9 | 10 | <RelativeLayout 11 | android:layout_width="0dp" 12 | android:layout_height="match_parent" 13 | android:layout_weight="0.5"> 14 | 15 | <TextView 16 | android:id="@+id/title_text" 17 | android:layout_width="match_parent" 18 | android:layout_height="match_parent" 19 | android:gravity="center" 20 | android:layout_gravity="center" 21 | android:textSize="15sp" /> 22 | 23 | </RelativeLayout> 24 | 25 | <RelativeLayout 26 | android:layout_width="0dp" 27 | android:layout_height="match_parent" 28 | android:layout_weight="0.1"> 29 | 30 | <TextView 31 | android:id="@+id/pass_or_fail_symbol_text" 32 | android:layout_width="match_parent" 33 | android:layout_height="match_parent" 34 | android:layout_gravity="center" 35 | android:gravity="center" 36 | android:text="TextView" 37 | android:textSize="18sp" /> 38 | 39 | </RelativeLayout> 40 | 41 | <Button 42 | android:id="@+id/details_button" 43 | android:layout_width="0dp" 44 | android:layout_height="match_parent" 45 | android:backgroundTint="#2A97FF" 46 | android:text="Details" 47 | android:textAlignment="center" 48 | android:textColor="#FFFFFF" 49 | android:layout_gravity="end" 50 | android:layout_weight="0.4" 51 | android:layout_marginStart="20dp" 52 | android:layout_marginEnd="20dp" /> 53 | </LinearLayout> -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Branch Android SDK for deep linking and attribution"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | android.url = "github:tadfisher/android-nixpkgs"; 8 | }; 9 | 10 | outputs = { self, nixpkgs, flake-utils, android }: flake-utils.lib.eachSystem [ "aarch64-darwin" "x86_64-darwin" "x86_64-linux" ] (system: 11 | let 12 | inherit (nixpkgs) lib; 13 | pkgs = import nixpkgs { 14 | inherit system; 15 | config.allowUnfree = true; 16 | overlays = [ 17 | (final: prev: { 18 | android-sdk = android.sdk.${system} (sdkPkgs: with sdkPkgs; [ 19 | build-tools-34-0-0 20 | cmdline-tools-latest 21 | emulator 22 | platform-tools 23 | platforms-android-34 24 | ] 25 | ++ lib.optionals (system == "aarch64-darwin") [ 26 | system-images-android-34-google-apis-arm64-v8a 27 | ] 28 | ++ lib.optionals (system == "x86_64-darwin" || system == "x86_64-linux") [ 29 | system-images-android-34-google-apis-x86-64 30 | ]); 31 | }) 32 | ]; 33 | }; 34 | in 35 | { 36 | formatter = pkgs.nixpkgs-fmt; 37 | devShells = { 38 | default = pkgs.mkShell { 39 | name = "branch-sdk-android"; 40 | ANDROID_HOME = "${pkgs.android-sdk}/share/android-sdk"; 41 | ANDROID_SDK_ROOT = "${pkgs.android-sdk}/share/android-sdk"; 42 | JAVA_HOME = "${pkgs.jdk17.home}"; 43 | shellHook = '' 44 | echo " 45 | Entered the Branch SDK Android development environment. 46 | "; 47 | ''; 48 | packages = [ 49 | pkgs.android-sdk 50 | pkgs.jdk17 # for android build-tools 51 | ]; 52 | }; 53 | }; 54 | } 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/ProductCategory.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | import android.text.TextUtils; 4 | 5 | /** 6 | * Created by Evan on 1/4/17. 7 | * <p> 8 | * Enumeration for product categories to be used with Branch commercial events 9 | * </p> 10 | */ 11 | 12 | public enum ProductCategory { 13 | ANIMALS_AND_PET_SUPPLIES("Animals & Pet Supplies"), 14 | APPAREL_AND_ACCESSORIES("Apparel & Accessories"), 15 | ARTS_AND_ENTERTAINMENT("Arts & Entertainment"), 16 | BABY_AND_TODDLER("Baby & Toddler"), 17 | BUSINESS_AND_INDUSTRIAL("Business & Industrial"), 18 | CAMERAS_AND_OPTICS("Cameras & Optics"), 19 | ELECTRONICS("Electronics"), 20 | FOOD_BEVERAGES_AND_TOBACCO("Food, Beverages & Tobacco"), 21 | FURNITURE("Furniture"), 22 | HARDWARE("Hardware"), 23 | HEALTH_AND_BEAUTY("Health & Beauty"), 24 | HOME_AND_GARDEN("Home & Garden"), 25 | LUGGAGE_AND_BAGS("Luggage & Bags"), 26 | MATURE("Mature"), 27 | MEDIA("Media"), 28 | OFFICE_SUPPLIES("Office Supplies"), 29 | RELIGIOUS_AND_CEREMONIAL("Religious & Ceremonial"), 30 | SOFTWARE("Software"), 31 | SPORTING_GOODS("Sporting Goods"), 32 | TOYS_AND_GAMES("Toys & Games"), 33 | VEHICLES_AND_PARTS("Vehicles & Parts"); 34 | 35 | private String name; 36 | 37 | ProductCategory(String name) { 38 | this.name = name; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public static ProductCategory getValue(String name) { 46 | ProductCategory productCategoryResult = null; 47 | if (!TextUtils.isEmpty(name)) { 48 | for (ProductCategory productCategory : ProductCategory.values()) { 49 | if (productCategory.name.equalsIgnoreCase(name)) { 50 | productCategoryResult = productCategory; 51 | break; 52 | } 53 | } 54 | } 55 | return productCategoryResult; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/CustomDomainCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.customDomainMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | import android.text.TextUtils; 7 | 8 | import org.json.JSONObject; 9 | 10 | public class CustomDomainCheck extends IntegrationValidatorCheck { 11 | 12 | String name = "Custom Domain"; 13 | String errorMessage = "Could not find intent filter to support Branch default link domain. Please add intent filter for handling custom link domain in your Android Manifest file"; 14 | String moreInfoLink = customDomainMoreInfoDocsLink; 15 | BranchIntegrationModel integrationModel; 16 | JSONObject branchAppConfig; 17 | 18 | public CustomDomainCheck(BranchIntegrationModel integrationModel, JSONObject branchAppConfig) { 19 | super.name = name; 20 | super.errorMessage = errorMessage; 21 | super.moreInfoLink = moreInfoLink; 22 | this.integrationModel = integrationModel; 23 | this.branchAppConfig = branchAppConfig; 24 | } 25 | 26 | @Override 27 | public boolean RunTests(Context context) { 28 | String customDomain = branchAppConfig.optString("short_url_domain"); 29 | return TextUtils.isEmpty(customDomain) || checkIfIntentAddedForLinkDomain(customDomain); 30 | } 31 | 32 | @Override 33 | public String GetOutput(Context context, boolean didTestSucceed) { 34 | didTestSucceed = RunTests(context); 35 | return super.GetOutput(context, didTestSucceed); 36 | } 37 | 38 | private boolean checkIfIntentAddedForLinkDomain(String domainName) { 39 | boolean foundIntentFilterMatchingDomainName = false; 40 | if (!TextUtils.isEmpty(domainName)) { 41 | for (String host : integrationModel.applinkScheme) { 42 | if (domainName.equals(host)) { 43 | foundIntentFilterMatchingDomainName = true; 44 | break; 45 | } 46 | } 47 | } 48 | return foundIntentFilterMatchingDomainName; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/DefaultDomainsCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.defaultDomainsMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | import android.text.TextUtils; 7 | 8 | import org.json.JSONObject; 9 | 10 | public class DefaultDomainsCheck extends IntegrationValidatorCheck { 11 | 12 | String name = "Default Domains"; 13 | String errorMessage = "Could not find any App Link hosts to support Android AppLinks. Please add intent filter for handling AppLinks in your Android Manifest file"; 14 | String moreInfoLink = defaultDomainsMoreInfoDocsLink; 15 | BranchIntegrationModel integrationModel; 16 | JSONObject branchAppConfig; 17 | 18 | public DefaultDomainsCheck(BranchIntegrationModel integrationModel, JSONObject branchAppConfig) { 19 | super.name = name; 20 | super.errorMessage = errorMessage; 21 | super.moreInfoLink = moreInfoLink; 22 | this.integrationModel = integrationModel; 23 | this.branchAppConfig = branchAppConfig; 24 | } 25 | 26 | @Override 27 | public boolean RunTests(Context context) { 28 | String defAppLinkDomain = branchAppConfig.optString("default_short_url_domain"); 29 | return TextUtils.isEmpty(defAppLinkDomain) || checkIfIntentAddedForLinkDomain(defAppLinkDomain); 30 | } 31 | 32 | @Override 33 | public String GetOutput(Context context, boolean didTestSucceed) { 34 | didTestSucceed = RunTests(context); 35 | return super.GetOutput(context, didTestSucceed); 36 | } 37 | 38 | private boolean checkIfIntentAddedForLinkDomain(String domainName) { 39 | boolean foundIntentFilterMatchingDomainName = false; 40 | if (!TextUtils.isEmpty(domainName)) { 41 | for (String host : integrationModel.applinkScheme) { 42 | if (domainName.equals(host)) { 43 | foundIntentFilterMatchingDomainName = true; 44 | break; 45 | } 46 | } 47 | } 48 | return foundIntentFilterMatchingDomainName; 49 | } 50 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/AlternateDomainsCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.alternateDomainsMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | import android.text.TextUtils; 7 | 8 | import org.json.JSONObject; 9 | 10 | public class AlternateDomainsCheck extends IntegrationValidatorCheck { 11 | String name = "Alt Domains"; 12 | String errorMessage = "Could not find intent filter to support alternate link domain. Please add intent filter for handling alternate link domain in your Android Manifest file"; 13 | String moreInfoLink = alternateDomainsMoreInfoDocsLink; 14 | BranchIntegrationModel integrationModel; 15 | JSONObject branchAppConfig; 16 | 17 | public AlternateDomainsCheck(BranchIntegrationModel integrationModel, JSONObject branchAppConfig) { 18 | super.name = name; 19 | super.errorMessage = errorMessage; 20 | super.moreInfoLink = moreInfoLink; 21 | this.integrationModel = integrationModel; 22 | this.branchAppConfig = branchAppConfig; 23 | } 24 | 25 | @Override 26 | public boolean RunTests(Context context) { 27 | String alternateAppLinkDomain = branchAppConfig.optString("alternate_short_url_domain"); 28 | return TextUtils.isEmpty(alternateAppLinkDomain) || checkIfIntentAddedForLinkDomain(alternateAppLinkDomain); 29 | } 30 | 31 | @Override 32 | public String GetOutput(Context context, boolean didTestSucceed) { 33 | didTestSucceed = RunTests(context); 34 | return super.GetOutput(context, didTestSucceed); 35 | } 36 | 37 | private boolean checkIfIntentAddedForLinkDomain(String domainName) { 38 | boolean foundIntentFilterMatchingDomainName = false; 39 | if (!TextUtils.isEmpty(domainName)) { 40 | for (String host : integrationModel.applinkScheme) { 41 | if (domainName.equals(host)) { 42 | foundIntentFilterMatchingDomainName = true; 43 | break; 44 | } 45 | } 46 | } 47 | return foundIntentFilterMatchingDomainName; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | assignees: [] 3 | body: 4 | - 5 | attributes: 6 | description: "What is the problem? A clear and concise description of what the bug is." 7 | label: "Describe the bug" 8 | placeholder: "Tell us what you see!" 9 | id: description 10 | type: textarea 11 | validations: 12 | required: true 13 | - 14 | attributes: 15 | description: "Please provide as much step-by-step detail as possible including logs, stack traces, and uncaught exceptions." 16 | label: "Steps to reproduce" 17 | value: | 18 | 1. 19 | 2. 20 | 3. 21 | id: steps 22 | type: textarea 23 | validations: 24 | required: true 25 | - 26 | attributes: 27 | description: "What did you expect to happen?" 28 | label: "Expected behavior" 29 | id: expected 30 | type: textarea 31 | validations: 32 | required: true 33 | - 34 | attributes: 35 | description: "What version of sdk are you seeing this issue on?" 36 | label: "SDK Version" 37 | placeholder: "5.2.3" 38 | id: sdk-version 39 | type: input 40 | validations: 41 | required: true 42 | - 43 | attributes: 44 | description: "What devices or emulators are you seeing this bug on?" 45 | label: Make and Model 46 | placeholder: "Samsung S21" 47 | id: device 48 | type: input 49 | validations: 50 | required: true 51 | - 52 | attributes: 53 | description: "What is the version of the Android OS?" 54 | label: OS 55 | placeholder: "12" 56 | id: os 57 | type: input 58 | validations: 59 | required: true 60 | - 61 | attributes: 62 | description: "Anything else that might be relevant for troubleshooting this bug. Any screenshots or videos that show the issue are very helpful." 63 | label: "Additional Information/Context" 64 | id: context 65 | type: textarea 66 | validations: 67 | required: false 68 | 69 | description: "Found a bug in the Branch Android SDK? File it here." 70 | labels: 71 | - bug 72 | - needs-triage 73 | name: "🐞 Bug report" 74 | title: "(short issue description)" 75 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/AppLinksCheck.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import static io.branch.referral.validators.IntegrationValidatorConstants.appLinksMoreInfoDocsLink; 4 | 5 | import android.content.Context; 6 | import android.text.TextUtils; 7 | 8 | import org.json.JSONObject; 9 | 10 | public class AppLinksCheck extends IntegrationValidatorCheck { 11 | 12 | String name = "App Links"; 13 | String errorMessage = "Could not find any App Link hosts to support Android AppLinks. Please add intent filter for handling AppLinks in your Android Manifest file"; 14 | String moreInfoLink = appLinksMoreInfoDocsLink; 15 | BranchIntegrationModel integrationModel; 16 | JSONObject branchAppConfig; 17 | 18 | public AppLinksCheck(BranchIntegrationModel integrationModel, JSONObject branchAppConfig) { 19 | super.name = name; 20 | super.errorMessage = errorMessage; 21 | super.moreInfoLink = moreInfoLink; 22 | this.integrationModel = integrationModel; 23 | this.branchAppConfig = branchAppConfig; 24 | } 25 | 26 | @Override 27 | public boolean RunTests(Context context) { 28 | String defAppLinkDomain = branchAppConfig.optString("default_short_url_domain"); 29 | return !integrationModel.applinkScheme.isEmpty() && !TextUtils.isEmpty(defAppLinkDomain) && checkIfIntentAddedForLinkDomain(defAppLinkDomain); 30 | } 31 | 32 | @Override 33 | public String GetOutput(Context context, boolean didTestSucceed) { 34 | didTestSucceed = RunTests(context); 35 | return super.GetOutput(context, didTestSucceed); 36 | } 37 | 38 | private boolean checkIfIntentAddedForLinkDomain(String domainName) { 39 | boolean foundIntentFilterMatchingDomainName = false; 40 | if (!TextUtils.isEmpty(domainName) && integrationModel.applinkScheme != null) { 41 | for (String host : integrationModel.applinkScheme) { 42 | if (domainName.equals(host)) { 43 | foundIntentFilterMatchingDomainName = true; 44 | break; 45 | } 46 | } 47 | } 48 | return foundIntentFilterMatchingDomainName; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/IntegrationValidatorConstants.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | public class IntegrationValidatorConstants { 4 | public static final String checkmark = "✅"; 5 | public static final String xmark = "❌"; 6 | public static final String appLinksMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#:~:text=%3C!%2D%2D%20Branch%20App%20Links%20%2D%20Live,%3C/intent%2Dfilter%3E\">More info</a>"; 7 | public static final String alternateDomainsMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#:~:text=The%20%2Dalternate.app.link%20domain%20associated%20with%20your%20app\">More info</a>"; 8 | public static final String branchInstanceCreationMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#:~:text=Branch.getAutoInstance(this)\">More info</a>"; 9 | public static final String branchKeysMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#:~:text=%3C!%2D%2D%20REPLACE%20%60BranchKey%60%20with,%22key_test_XXX%22%20/%3E\">More info</a>"; 10 | public static final String customDomainMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#section-configure-app\">More info</a>"; 11 | public static final String defaultDomainsMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#4-configure-app:~:text=%3C!%2D%2D%20Branch%20App%20Links%20%2D%20Live,%3C/intent%2Dfilter%3E\">More info</a>"; 12 | public static final String packageNameMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#:~:text=package%3D%22com.example.android%22\">More info</a>"; 13 | public static final String uriSchemeAppMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#4-configure-app:~:text=%3Cintent%2Dfilter%3E%0A%09%09%09%09%3C!%2D%2D%20If,/%3E%0A%09%09%09%3C/intent%2Dfilter%3E\">More info</a>"; 14 | public static final String uriSchemeDashboardMoreInfoDocsLink = "<a href=\"https://help.branch.io/developers-hub/docs/android-basic-integration#1-configure-branch-dashboard:~:text=%22Android%20URI%20Scheme%22\">More info</a>"; 15 | } 16 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchPartnerParameters.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import androidx.annotation.NonNull; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class BranchPartnerParameters { 9 | private final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> partnerParameters = new ConcurrentHashMap<>(); 10 | 11 | void clearAllParameters() { 12 | partnerParameters.clear(); 13 | } 14 | 15 | @NonNull ConcurrentHashMap<String, String> parametersForPartner(@NonNull String key) { 16 | ConcurrentHashMap<String, String> res = partnerParameters.get(key); 17 | if (res == null) { 18 | res = new ConcurrentHashMap<>(); 19 | partnerParameters.put(key, res); 20 | } 21 | return res; 22 | } 23 | 24 | private void addParameterWithName(@NonNull String key, @NonNull String value, @NonNull String partnerName) { 25 | parametersForPartner(partnerName).put(key, value); 26 | } 27 | 28 | void addFacebookParameter(@NonNull String key, @NonNull String value) { 29 | if (isSha256Hashed(value)) { 30 | addParameterWithName(key, value, "fb"); 31 | } else { 32 | BranchLogger.w("Invalid partner parameter passed. Value must be a SHA 256 hash."); 33 | } 34 | } 35 | 36 | void addSnapParameter(@NonNull String key, @NonNull String value) { 37 | if (isSha256Hashed(value)) { 38 | addParameterWithName(key, value, "snap"); 39 | } else { 40 | BranchLogger.w("Invalid partner parameter passed. Value must be a SHA 256 hash."); 41 | } 42 | } 43 | 44 | boolean isSha256Hashed(String value) { 45 | return value != null && value.length() == 64 && isHexadecimal(value); 46 | } 47 | 48 | private static final Pattern HEXADECIMAL_PATTERN = Pattern.compile("\\p{XDigit}+"); 49 | boolean isHexadecimal(String input) { 50 | if (input == null) return false; 51 | if (input.length() == 0) return true; 52 | final Matcher matcher = HEXADECIMAL_PATTERN.matcher(input); 53 | return matcher.matches(); 54 | } 55 | 56 | ConcurrentHashMap<String, ConcurrentHashMap<String, String>> allParams() { 57 | return partnerParameters; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/QRCode/ServerRequestCreateQRCode.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.QRCode; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import org.json.JSONObject; 7 | 8 | import io.branch.referral.Branch; 9 | import io.branch.referral.Defines; 10 | import io.branch.referral.ServerRequest; 11 | import io.branch.referral.ServerResponse; 12 | 13 | public class ServerRequestCreateQRCode extends ServerRequest { 14 | 15 | private JSONObject params_; 16 | final Defines.RequestPath requestPath_; 17 | private long queueWaitTime_ = 0; 18 | private final Context context_; 19 | private BranchQRCode.BranchQRCodeRequestHandler callback_; 20 | 21 | /** 22 | * <p>Creates an instance of ServerRequest.</p> 23 | * @param requestPath Path to server for this request. 24 | * @param post A {@link JSONObject} containing the post data supplied with the current request 25 | * as key-value pairs. 26 | * @param context Application context. 27 | * @param callback A {@link } callback instance that will trigger 28 | */ 29 | protected ServerRequestCreateQRCode(Defines.RequestPath requestPath, JSONObject post, Context context, BranchQRCode.BranchQRCodeRequestHandler callback) { 30 | super(Defines.RequestPath.QRCode, post, context); 31 | context_ = context; 32 | requestPath_ = requestPath; 33 | params_ = post; 34 | callback_ = callback; 35 | } 36 | 37 | @Override 38 | public boolean handleErrors(Context context) { 39 | return false; 40 | } 41 | 42 | @Override 43 | public void onRequestSucceeded(ServerResponse response, Branch branch) { 44 | callback_.onDataReceived(response); 45 | } 46 | 47 | @Override 48 | public void handleFailure(int statusCode, String causeMsg) { 49 | Exception e = new Exception("Failed server request: " + statusCode + causeMsg); 50 | callback_.onFailure(e); 51 | } 52 | 53 | @Override 54 | /** 55 | * Called when request is added to the queue 56 | */ 57 | public void onRequestQueued() { 58 | queueWaitTime_ = System.currentTimeMillis(); 59 | } 60 | 61 | @Override 62 | public boolean isGetRequest() { 63 | return false; 64 | } 65 | 66 | @Override 67 | public void clearCallbacks() { 68 | callback_ = null; 69 | } 70 | 71 | @Override 72 | protected boolean prepareExecuteWithoutTracking() { 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/BillingGooglePlayTests.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import com.android.billingclient.api.Purchase 5 | import io.branch.referral.util.CurrencyType 6 | 7 | import org.junit.Assert 8 | import org.junit.Before 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | 12 | @RunWith(AndroidJUnit4::class) 13 | class BillingGooglePlayTests : BranchTest() { 14 | 15 | @Before 16 | fun initializeValues() { 17 | initBranchInstance() 18 | } 19 | 20 | @Test 21 | fun testCreateAndLogEventWithPurchase() { 22 | val purchaseJsonString = 23 | "{\"orderId\":\"GPA.3363-6943-2756-89326\",\"packageName\":\"io.branch.referral.test\",\"productId\":\"one_button_click\",\"purchaseTime\":1677102142024,\"purchaseState\":0,\"purchaseToken\":\"ehnaiklogbdnkmeeichalhgi.AO-J1OygqRTr05Xd7Tuiwk5sSg302sRmiM_zcVcsXdkGyUxlIkwVR0y2j0xfY_1LovxU5qIoJ2iNwILe5H3WvPTGOjESP378uQ\",\"quantity\":1,\"acknowledged\":false}" 24 | val purchaseSignature = 25 | "XDFlSNC9Gqs+PPmO3xOFdLMaQ4FbsBEpTxBuOd+6adEEcz5Uovlgep+F5Xbr08+x/xzCEyNzybDYDcNg/PTzwfoK6Aeq44mocW4CPA1w/r1rdmgtwBD8nAdWIr3BbwXmcl6LYEGA6dL0N+/3zzjNzK/VWdqXazSdRyXxtlHnx8wsBFdPCBs1e9LtEwUcganA6ot0ttO2ySCKYNne2pEm2ScU+uuWZqZJ00VM7KH9pT+SKOOlSs6rRuFEvbGsoPUdybZQ0WoiXg6JD2hz9/35mQJF4Lkjh2kVgTh5MV4sCNnbMuUmhX/d09+pK2Fw6xiUng3FClOetFV9MaTtsmbz/g==" 26 | val mockPurchase = Purchase(purchaseJsonString, purchaseSignature) 27 | 28 | BillingGooglePlay.getInstance().createAndLogEventForPurchase(testContext, mockPurchase, listOf(), CurrencyType.USD, 99.99, "IAP") 29 | 30 | val queue = ServerRequestQueue.getInstance(testContext) 31 | val eventRequest = queue.peekAt(0) 32 | val customData = eventRequest.getParams.getJSONObject("custom_data") 33 | val eventData = eventRequest.getParams.getJSONObject("event_data") 34 | 35 | Assert.assertEquals("true", customData.get("logged_from_IAP")) 36 | Assert.assertEquals("io.branch.referral.test", customData.get("package_name")) 37 | Assert.assertEquals("GPA.3363-6943-2756-89326", customData.get("order_id")) 38 | 39 | Assert.assertEquals(99.99, eventData.get("revenue")) 40 | Assert.assertEquals("USD", eventData.get("currency")) 41 | 42 | Assert.assertEquals(1, queue.size.toLong()) 43 | Assert.assertEquals( 44 | Defines.RequestPath.TrackStandardEvent.path, 45 | eventRequest.requestPath 46 | ) 47 | Assert.assertTrue(eventRequest.isWaitingOnProcessToFinish) 48 | } 49 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/QueueOperationLogout.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral 2 | 3 | import android.content.Context 4 | import io.branch.referral.Branch.LogoutStatusListener 5 | import io.branch.referral.BranchLogger.e 6 | import io.branch.referral.BranchLogger.v 7 | import io.branch.referral.Defines.RequestPath 8 | 9 | /** 10 | * This class implements the logout feature through the legacy queueing system. 11 | * To maintain queue compatibility and reduce complexity, do not invoke network task as operation is 12 | * completely client side. 13 | */ 14 | class QueueOperationLogout( 15 | var context_: Context, 16 | requestPath: RequestPath?, 17 | var callback_: LogoutStatusListener? 18 | ) : 19 | ServerRequest(context_, requestPath) { 20 | override fun onPreExecute() { 21 | //NO-OP 22 | } 23 | 24 | public override fun doFinalUpdateOnBackgroundThread() { 25 | //NO-OP 26 | } 27 | 28 | public override fun doFinalUpdateOnMainThread() { 29 | try { 30 | v("doFinalUpdateOnMainThread $this") 31 | val prefHelper_ = PrefHelper.getInstance(context_) 32 | prefHelper_.identity = PrefHelper.NO_STRING_VALUE 33 | v("Identity set to: " + prefHelper_.identity) 34 | prefHelper_.clearUserValues() 35 | } catch (e: Exception) { 36 | e("Caught Exception: doFinalUpdateOnMainThread " + this + " " + e.message) 37 | } 38 | } 39 | 40 | override fun handleErrors(context: Context): Boolean { 41 | return false 42 | } 43 | 44 | override fun onRequestSucceeded(response: ServerResponse, branch: Branch) { 45 | v("QueueOperationLogout onRequestSucceeded $this") 46 | if (callback_ != null) { 47 | callback_!!.onLogoutFinished(true, null) 48 | } 49 | } 50 | 51 | override fun handleFailure(statusCode: Int, causeMsg: String) { 52 | v("QueueOperationLogout handleFailure $this") 53 | if (callback_ != null) { 54 | v("QueueOperationLogout handleFailure $this") 55 | val currentIdentity = prefHelper_.identity 56 | val error = BranchError("Error in logout method. Current identity value: $currentIdentity $causeMsg", -1) 57 | callback_!!.onLogoutFinished(true, error) 58 | } 59 | } 60 | 61 | override fun isGetRequest(): Boolean { 62 | return false 63 | } 64 | 65 | override fun clearCallbacks() { 66 | } 67 | 68 | // For backwards compat with deprecated disableTracking 69 | override fun prepareExecuteWithoutTracking(): Boolean { 70 | return true 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/CustomBranchApp.java: -------------------------------------------------------------------------------- 1 | package io.branch.branchandroidtestbed; 2 | 3 | import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK; 4 | 5 | import android.app.Application; 6 | import android.util.Log; 7 | 8 | import androidx.browser.customtabs.CustomTabsIntent; 9 | 10 | import org.json.JSONObject; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.OutputStreamWriter; 15 | 16 | import io.branch.referral.Branch; 17 | import io.branch.referral.BranchLogger; 18 | import io.branch.referral.IBranchRequestTracingCallback; 19 | 20 | public final class CustomBranchApp extends Application { 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | 25 | // IBranchLoggingCallbacks loggingCallbacks = (message, tag) -> { 26 | // Log.d("BranchTestbed", message); 27 | // saveLogToFile(message); 28 | // }; 29 | Branch.enableLogging(BranchLogger.BranchLogLevel.VERBOSE); 30 | Branch.getAutoInstance(this); 31 | Branch.expectDelayedSessionInitialization(true); 32 | CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder() 33 | .setColorScheme(COLOR_SCHEME_DARK) 34 | .build(); 35 | Branch.getInstance().setCustomTabsIntent(customTabsIntent); 36 | Branch.setCallbackForTracingRequests(new IBranchRequestTracingCallback() { 37 | @Override 38 | public void onRequestCompleted(String uri, JSONObject request, JSONObject response, String error, String requestUrl) { 39 | Log.d("Shortlink_Session_Test", 40 | "URI Sent to Branch: " + uri 41 | + "\nRequest: " + request 42 | + "\nResponse: " + response 43 | + "\nError Message: " + error 44 | + "\nRequest Url: " + requestUrl 45 | ); 46 | } 47 | }); 48 | } 49 | 50 | private void saveLogToFile(String logMessage) { 51 | File logFile = new File(getFilesDir(), "branchlogs.txt"); 52 | 53 | try { 54 | if (!logFile.exists()) { 55 | boolean fileCreated = logFile.createNewFile(); 56 | Log.d("BranchTestbed", "Log file created: " + fileCreated); 57 | } 58 | 59 | try (FileOutputStream fos = new FileOutputStream(logFile, true); 60 | OutputStreamWriter writer = new OutputStreamWriter(fos)) { 61 | writer.write(logMessage + "\n"); 62 | } 63 | 64 | } catch (Exception e) { 65 | Log.e("BranchTestbed", "Error writing to log file", e); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/java/io/branch/branchandroidtestbed/LogOutputActivity.java: -------------------------------------------------------------------------------- 1 | package io.branch.branchandroidtestbed; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | import android.widget.TextView; 8 | import android.widget.Toast; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.InputStreamReader; 14 | 15 | public class LogOutputActivity extends Activity { 16 | private TextView logOutputTextView; 17 | private File logFile; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_log_output); 23 | 24 | logOutputTextView = findViewById(R.id.logOutputTextView); 25 | logFile = new File(getFilesDir(), "branchlogs.txt"); 26 | displayLogs(); 27 | } 28 | 29 | @Override 30 | public boolean onCreateOptionsMenu(Menu menu) { 31 | getMenuInflater().inflate(R.menu.menu_log_output, menu); 32 | return true; 33 | } 34 | 35 | @Override 36 | public boolean onOptionsItemSelected(MenuItem item) { 37 | if (item.getItemId() == R.id.action_clear_logs) { 38 | clearLogs(); 39 | return true; 40 | } 41 | return super.onOptionsItemSelected(item); 42 | } 43 | 44 | private void displayLogs() { 45 | if (logFile.exists()) { 46 | StringBuilder logContent = new StringBuilder(); 47 | 48 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(logFile)))) { 49 | String line; 50 | while ((line = reader.readLine()) != null) { 51 | logContent.append(line).append("\n"); 52 | } 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | logContent.append("Error reading log file."); 56 | } 57 | 58 | logOutputTextView.setText(logContent.toString()); 59 | } else { 60 | logOutputTextView.setText("Log file not found."); 61 | } 62 | } 63 | 64 | private void clearLogs() { 65 | if (logFile.exists()) { 66 | if (logFile.delete()) { 67 | Toast.makeText(this, "Logs cleared.", Toast.LENGTH_SHORT).show(); 68 | logOutputTextView.setText("Logs cleared."); 69 | finish(); 70 | } else { 71 | Toast.makeText(this, "Failed to clear logs.", Toast.LENGTH_SHORT).show(); 72 | } 73 | } else { 74 | Toast.makeText(this, "No log file to clear.", Toast.LENGTH_SHORT).show(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/BranchPluginSupportTest.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | @RunWith(AndroidJUnit4.class) 14 | public class BranchPluginSupportTest extends BranchTest { 15 | 16 | Map<String, Object> deviceData = new HashMap<>(); 17 | @Before 18 | public void initializeValues(){ 19 | //assign map 20 | initBranchInstance(); 21 | deviceData = BranchPluginSupport.getInstance().deviceDescription(); 22 | } 23 | 24 | @Test 25 | public void testBranchPluginSupportExists() { 26 | Assert.assertNotNull(BranchPluginSupport.getInstance()); 27 | } 28 | 29 | @Test 30 | public void testHardwareIdDebug() { 31 | SystemObserver.UniqueId uniqueId1 = BranchPluginSupport.getInstance().getHardwareID(); 32 | SystemObserver.UniqueId uniqueId2 = BranchPluginSupport.getInstance().getHardwareID(); 33 | Assert.assertEquals(uniqueId1, uniqueId2); 34 | } 35 | 36 | @Test 37 | public void testAppVersion() { 38 | Assert.assertEquals("bnc_no_value", deviceData.get("app_version")); 39 | } 40 | 41 | @Test 42 | public void testCountry() { 43 | Assert.assertNotNull(deviceData.get("country")); 44 | } 45 | 46 | @Test 47 | public void testOSVersion() { 48 | Assert.assertNotNull(deviceData.get("os_version_android")); 49 | } 50 | 51 | @Test 52 | public void testScreenWidth() { 53 | Assert.assertNotNull(deviceData.get("screen_width")); 54 | } 55 | 56 | @Test 57 | public void testScreenHeight() { 58 | Assert.assertNotNull(deviceData.get("screen_height")); 59 | } 60 | 61 | @Test 62 | public void testScreenDPI() { 63 | Assert.assertNotNull(deviceData.get("screen_dpi")); 64 | } 65 | 66 | @Test 67 | public void testOS() { 68 | String os = (String) deviceData.get("os"); 69 | Assert.assertNotNull(os); 70 | 71 | if(SystemObserver.isFireOSDevice()){ 72 | Assert.assertTrue(os.toLowerCase().contains("amazon")); 73 | } 74 | else { 75 | Assert.assertEquals("Android", deviceData.get("os")); 76 | } 77 | } 78 | 79 | @Test 80 | public void testLanguage() { 81 | Assert.assertNotNull(deviceData.get("language")); 82 | } 83 | 84 | @Test 85 | public void testModel() { 86 | Assert.assertNotNull(deviceData.get("model")); 87 | } 88 | 89 | @Test 90 | public void testAndroidID() { 91 | Assert.assertNotNull(deviceData.get("android_id")); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/Product.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | /** 7 | * Created by Evan Groth on 12/21/16. 8 | */ 9 | 10 | public class Product { 11 | private String sku; 12 | private String name; 13 | private Double price; 14 | private Integer quantity; 15 | private String brand; 16 | private String variant; 17 | private ProductCategory category; 18 | 19 | public String getSku() { 20 | return sku; 21 | } 22 | 23 | public void setSku(String sku) { 24 | this.sku = sku; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | public Double getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(Double price) { 40 | this.price = price; 41 | } 42 | 43 | public Integer getQuantity() { 44 | return quantity; 45 | } 46 | 47 | public void setQuantity(Integer quantity) { 48 | this.quantity = quantity; 49 | } 50 | 51 | public String getBrand() { 52 | return brand; 53 | } 54 | 55 | public void setBrand(String brand) { 56 | this.brand = brand; 57 | } 58 | 59 | public String getVariant() { 60 | return variant; 61 | } 62 | 63 | public void setVariant(String variant) { 64 | this.variant = variant; 65 | } 66 | 67 | public ProductCategory getCategory() { 68 | return category; 69 | } 70 | 71 | public void setCategory(ProductCategory category) { 72 | this.category = category; 73 | } 74 | 75 | public Product() { 76 | } 77 | 78 | public Product(String sku, String name, Double price, Integer quantity, String brand, String variant, ProductCategory category) { 79 | this.sku = sku; 80 | this.name = name; 81 | this.price = price; 82 | this.quantity = quantity; 83 | this.brand = brand; 84 | this.variant = variant; 85 | this.category = category; 86 | } 87 | 88 | public JSONObject getProductJSONObject() { 89 | JSONObject jsonObject = new JSONObject(); 90 | try { 91 | jsonObject.put("sku", this.sku); 92 | jsonObject.put("name", this.name); 93 | jsonObject.put("price", this.price); 94 | jsonObject.put("quantity", this.quantity); 95 | jsonObject.put("brand", this.brand); 96 | jsonObject.put("variant", this.variant); 97 | jsonObject.put("category", this.category); 98 | } catch (JSONException e) { 99 | 100 | } 101 | return jsonObject; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.* 2 | 3 | plugins { 4 | id("com.android.application") 5 | kotlin("android") 6 | } 7 | 8 | dependencies { 9 | implementation(project(":Branch-SDK")) 10 | implementation("com.google.android.gms:play-services-ads-identifier:18.0.1") 11 | implementation("com.huawei.hms:ads-identifier:3.4.62.300") 12 | 13 | implementation("com.android.billingclient:billing:6.0.1") 14 | implementation("com.huawei.hms:ads-installreferrer:3.4.39.302") 15 | implementation("store.galaxy.samsung.installreferrer:samsung_galaxystore_install_referrer:4.0.0") 16 | implementation("com.miui.referrer:homereferrer:1.0.0.7") 17 | 18 | implementation("androidx.browser:browser:1.8.0") 19 | 20 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 21 | androidTestImplementation("androidx.test:runner:1.5.2") 22 | androidTestImplementation("androidx.test:rules:1.5.0") 23 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 24 | } 25 | 26 | android { 27 | val ANDROID_BUILD_SDK_VERSION_COMPILE: String by project 28 | val ANDROID_BUILD_SDK_VERSION_MINIMUM: String by project 29 | val VERSION_NAME: String by project 30 | val VERSION_CODE: String by project 31 | 32 | compileSdk = ANDROID_BUILD_SDK_VERSION_COMPILE.toInt() 33 | defaultConfig { 34 | applicationId = "io.branch.branchandroidtestbed" 35 | minSdk = ANDROID_BUILD_SDK_VERSION_MINIMUM.toInt() 36 | targetSdk = 34 37 | versionName = VERSION_NAME 38 | versionCode = VERSION_CODE.toInt() 39 | 40 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 41 | } 42 | 43 | val propFile = file("signing.properties") 44 | val props = if (propFile.exists()) { 45 | val props = Properties() 46 | props.load(propFile.inputStream()) 47 | props 48 | } else { 49 | null 50 | } 51 | 52 | if (props != null) { 53 | val pStoreFile = props["STORE_FILE"] as? String 54 | val pStorePassword = props["STORE_PASSWORD"] as? String 55 | val pKeyAlias = props["KEY_ALIAS"] as? String 56 | val pKeyPassword = props["KEY_PASSWORD"] as? String 57 | if (!pStoreFile.isNullOrBlank() 58 | && !pStorePassword.isNullOrBlank() 59 | && !pKeyAlias.isNullOrBlank() 60 | && !pKeyPassword.isNullOrBlank() 61 | ) { 62 | signingConfigs { 63 | create("release") { 64 | storeFile = file(pStoreFile) 65 | storePassword = pStorePassword 66 | keyAlias = pKeyAlias 67 | keyPassword = pKeyPassword 68 | } 69 | } 70 | } 71 | } 72 | 73 | buildTypes { 74 | release { 75 | signingConfig = signingConfigs.findByName("release") 76 | } 77 | } 78 | namespace = "io.branch.branchandroidtestbed" 79 | } 80 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetLATD.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | public class ServerRequestGetLATD extends ServerRequest { 9 | 10 | private BranchLastAttributedTouchDataListener callback; 11 | // defaultAttributionWindow is the "default" for the SDK's side, server interprets it as 30 days 12 | protected static final int defaultAttributionWindow = -1; 13 | private int attributionWindow; 14 | 15 | ServerRequestGetLATD(Context context, Defines.RequestPath requestPath, BranchLastAttributedTouchDataListener callback) { 16 | this(context, requestPath, callback, PrefHelper.getInstance(context).getLATDAttributionWindow()); 17 | } 18 | 19 | ServerRequestGetLATD(Context context, Defines.RequestPath requestPath, 20 | BranchLastAttributedTouchDataListener callback, int attributionWindow) { 21 | super(context, requestPath); 22 | this.callback = callback; 23 | this.attributionWindow = attributionWindow; 24 | JSONObject reqBody = new JSONObject(); 25 | try { 26 | setPost(reqBody); 27 | } catch (JSONException e) { 28 | BranchLogger.w("Caught JSONException " + e.getMessage()); 29 | } 30 | updateEnvironment(context, reqBody); 31 | } 32 | 33 | protected int getAttributionWindow() { 34 | return attributionWindow; 35 | } 36 | 37 | @Override 38 | public boolean handleErrors(Context context) { 39 | return false; 40 | } 41 | 42 | @Override 43 | public void onRequestSucceeded(ServerResponse response, Branch branch) { 44 | if (callback == null) { 45 | return; 46 | } 47 | 48 | if (response != null) { 49 | callback.onDataFetched(response.getObject(), null); 50 | } else { 51 | handleFailure(BranchError.ERR_BRANCH_INVALID_REQUEST, "Failed to get last attributed touch data"); 52 | } 53 | } 54 | 55 | @Override 56 | public void handleFailure(int statusCode, String causeMsg) { 57 | if (callback != null) { 58 | callback.onDataFetched(null, new BranchError("Failed to get last attributed touch data", statusCode)); 59 | } 60 | } 61 | 62 | @Override 63 | public boolean isGetRequest() { 64 | return false; 65 | } 66 | 67 | @Override 68 | public void clearCallbacks() { 69 | callback = null; 70 | } 71 | 72 | @Override 73 | public BRANCH_API_VERSION getBranchRemoteAPIVersion() { 74 | return BRANCH_API_VERSION.V1_LATD; 75 | } 76 | 77 | @Override 78 | protected boolean shouldUpdateLimitFacebookTracking() { 79 | return true; 80 | } 81 | 82 | public interface BranchLastAttributedTouchDataListener { 83 | void onDataFetched(JSONObject jsonObject, BranchError error); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Branch-SDK/doc/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"> 2 | <!-- NewPage --> 3 | <html lang="en"> 4 | <head> 5 | <!-- Generated by javadoc (1.8.0_25) on Mon Apr 13 12:03:28 EDT 2015 --> 6 | <title>Generated Documentation (Untitled) 7 | 59 | 60 | 61 | 62 | 63 | 64 | <noscript> 65 | <div>JavaScript is disabled on your browser.</div> 66 | </noscript> 67 | <h2>Frame Alert</h2> 68 | <p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href="io/branch/referral/package-summary.html">Non-frame version</a>.</p> 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/QueueOperationSetIdentity.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral 2 | 3 | import android.content.Context 4 | import io.branch.referral.Branch.BranchReferralInitListener 5 | import io.branch.referral.BranchLogger.e 6 | import io.branch.referral.BranchLogger.v 7 | import io.branch.referral.Defines.RequestPath 8 | import org.json.JSONObject 9 | 10 | /** 11 | * This class implements the setIdentity feature through the legacy queueing system. 12 | * To maintain queue compatibility and reduce complexity, do not invoke network task as operation is 13 | * completely client side. 14 | */ 15 | class QueueOperationSetIdentity( 16 | var context_: Context, 17 | requestPath: RequestPath?, 18 | var userId_: String?, 19 | var callback_: BranchReferralInitListener? 20 | ) : 21 | ServerRequest(context_, requestPath) { 22 | override fun onPreExecute() { 23 | //NO-OP 24 | } 25 | 26 | public override fun doFinalUpdateOnBackgroundThread() { 27 | //NO-OP 28 | } 29 | 30 | public override fun doFinalUpdateOnMainThread() { 31 | try { 32 | v("doFinalUpdateOnMainThread $this") 33 | if (userId_ != null) { 34 | Branch.installDeveloperId = userId_ 35 | prefHelper_.identity = userId_ 36 | v("Identity is now set to: " + Branch.installDeveloperId) 37 | } 38 | } catch (e: Exception) { 39 | e("Caught Exception: doFinalUpdateOnMainThread " + this + " " + e.message) 40 | } 41 | } 42 | 43 | override fun handleErrors(context: Context): Boolean { 44 | return false 45 | } 46 | 47 | override fun onRequestSucceeded(response: ServerResponse, branch: Branch) { 48 | v("onRequestSucceeded $this") 49 | if (callback_ != null) { 50 | var latestReferringParams: JSONObject? = null 51 | try { 52 | latestReferringParams = branch.firstReferringParams 53 | } catch (e: Exception) { 54 | e("Caught exception " + this + " onRequestSucceeded: " + e.message) 55 | } 56 | callback_!!.onInitFinished(latestReferringParams, null) 57 | } 58 | } 59 | 60 | override fun handleFailure(statusCode: Int, causeMsg: String) { 61 | v("QueueOperationSetIdentity handleFailure $this") 62 | if (callback_ != null) { 63 | v("QueueOperationSetIdentity handleFailure $this") 64 | val currentIdentity = prefHelper_.identity 65 | val error = BranchError("Error in setIdentity method. Current identity value: $currentIdentity $causeMsg", -1) 66 | callback_!!.onInitFinished(JSONObject(), error) 67 | } 68 | } 69 | 70 | override fun isGetRequest(): Boolean { 71 | return false 72 | } 73 | 74 | override fun clearCallbacks() { 75 | } 76 | 77 | // For backwards compat with deprecated disableTracking 78 | override fun prepareExecuteWithoutTracking(): Boolean { 79 | return true 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/BranchIntegrationModel.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import android.content.Context; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | 7 | import org.json.JSONArray; 8 | import org.json.JSONObject; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Objects; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import io.branch.referral.BranchAsyncTask; 16 | import io.branch.referral.BranchLogger; 17 | import io.branch.referral.BranchUtil; 18 | import io.branch.referral.Defines; 19 | 20 | class BranchIntegrationModel { 21 | JSONObject deeplinkUriScheme; 22 | private final String branchKeyTest; 23 | private final String branchKeyLive; 24 | final List applinkScheme; 25 | final String packageName; 26 | boolean appSettingsAvailable = false; 27 | 28 | 29 | public BranchIntegrationModel(Context context) { 30 | String liveKey = null, testKey = null; 31 | ApplicationInfo appInfo; 32 | applinkScheme = new ArrayList<>(); 33 | packageName = context.getPackageName(); 34 | 35 | try { 36 | appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); 37 | if (appInfo.metaData != null) { 38 | liveKey = appInfo.metaData.getString("io.branch.sdk.BranchKey"); 39 | testKey = appInfo.metaData.getString("io.branch.sdk.BranchKey.test"); 40 | } 41 | } catch (PackageManager.NameNotFoundException e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | branchKeyLive = liveKey; 46 | branchKeyTest = testKey; 47 | updateDeepLinkSchemes(context); 48 | } 49 | 50 | private void updateDeepLinkSchemes(Context context) { 51 | JSONObject obj = null; 52 | try { 53 | // Avoid ANRs on reading and parsing manifest with a timeout 54 | obj = new getDeepLinkSchemeTasks().executeTask(context).get(2500, TimeUnit.MILLISECONDS); 55 | appSettingsAvailable = true; 56 | } catch (Exception e) { 57 | BranchLogger.d(e.getMessage()); 58 | } 59 | if (obj != null) { 60 | deeplinkUriScheme = obj.optJSONObject(Defines.Jsonkey.URIScheme.getKey()); 61 | JSONArray hostArray = obj.optJSONArray(Defines.Jsonkey.AppLinks.getKey()); 62 | if (hostArray != null) { 63 | for (int i = 0; i < hostArray.length(); i++) { 64 | applinkScheme.add(hostArray.optString(i)); 65 | } 66 | } 67 | } 68 | } 69 | 70 | // Reading deep linked schemes involves decompressing of apk and parsing manifest. This can lead to a ANR if reading file is slower 71 | // Use this only with a timeout 72 | private class getDeepLinkSchemeTasks extends BranchAsyncTask { 73 | @Override 74 | protected JSONObject doInBackground(Context... contexts) { 75 | return BranchUtil.getDeepLinkSchemes(contexts[0]); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/AdvertisingIdTests.kt: -------------------------------------------------------------------------------- 1 | package io.branch.referral 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import com.google.android.gms.common.ConnectionResult 5 | import com.google.android.gms.common.GoogleApiAvailabilityLight 6 | import com.huawei.hms.api.HuaweiApiAvailability 7 | import io.branch.coroutines.getAmazonFireAdvertisingInfoObject 8 | import io.branch.coroutines.getGoogleAdvertisingInfoObject 9 | import io.branch.coroutines.getHuaweiAdvertisingInfoObject 10 | import io.branch.referral.SystemObserver.isFireOSDevice 11 | import kotlinx.coroutines.ExperimentalCoroutinesApi 12 | import kotlinx.coroutines.test.runTest 13 | import org.junit.Assert 14 | import org.junit.Test 15 | import org.junit.runner.RunWith 16 | 17 | @OptIn(ExperimentalCoroutinesApi::class) 18 | @RunWith(AndroidJUnit4::class) 19 | class AdvertisingIdTests: BranchTest() { 20 | 21 | @Test 22 | fun googleAdvertisingInfoObjectNotNullWhenServiceAvailable() = runTest { 23 | if(GoogleApiAvailabilityLight.getInstance().isGooglePlayServicesAvailable(testContext) == ConnectionResult.SUCCESS) { 24 | val googleAdvertisingInfoObject = getGoogleAdvertisingInfoObject(testContext) 25 | Assert.assertNotNull(googleAdvertisingInfoObject) 26 | } 27 | } 28 | 29 | @Test 30 | fun googleAdvertisingInfoObjectNullWhenServiceUnavailable() = runTest { 31 | if(GoogleApiAvailabilityLight.getInstance().isGooglePlayServicesAvailable(testContext) != ConnectionResult.SUCCESS) { 32 | val googleAdvertisingInfoObject = getGoogleAdvertisingInfoObject(testContext) 33 | Assert.assertNull(googleAdvertisingInfoObject) 34 | } 35 | } 36 | 37 | @Test 38 | fun huaweiAdvertisingInfoObjectNotNullWhenServiceAvailable() = runTest { 39 | if(HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(testContext) == com.huawei.hms.api.ConnectionResult.SUCCESS) { 40 | val huaweiAdvertisingObject = getHuaweiAdvertisingInfoObject(testContext) 41 | Assert.assertNotNull(huaweiAdvertisingObject) 42 | } 43 | } 44 | 45 | @Test 46 | fun huaweiAdvertisingInfoObjectNullWhenServiceUnavailable() = runTest { 47 | if(HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(testContext) != com.huawei.hms.api.ConnectionResult.SUCCESS) { 48 | val huaweiAdvertisingObject = getHuaweiAdvertisingInfoObject(testContext) 49 | Assert.assertNull(huaweiAdvertisingObject) 50 | } 51 | } 52 | 53 | @Test 54 | fun amazonAdvertisingInfoObjectNotNullWhenServiceAvailable() = runTest { 55 | if(isFireOSDevice()) { 56 | val amazonAdvertisingObject = getAmazonFireAdvertisingInfoObject(testContext) 57 | Assert.assertNotNull(amazonAdvertisingObject) 58 | } 59 | } 60 | 61 | @Test 62 | fun amazonAdvertisingInfoObjectNullWhenServiceUnavailable() = runTest { 63 | if(!isFireOSDevice()) { 64 | val amazonAdvertisingObject = getAmazonFireAdvertisingInfoObject(testContext) 65 | Assert.assertNull(amazonAdvertisingObject) 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/CurrencyType.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | import android.text.TextUtils; 4 | 5 | /** 6 | * Created by sojanpr on 8/31/16. 7 | * ISO 4217 code enumeration for currency 8 | */ 9 | public enum CurrencyType { 10 | AED("AED"), AFN("AFN"), ALL("ALL"), AMD("AMD"), ANG("ANG"), AOA("AOA"), ARS("ARS"), AUD("AUD"), AWG("AWG"), AZN("AZN"), BAM("BAM"), BBD("BBD"), 11 | BDT("BDT"), BGN("BGN"), BHD("BHD"), BIF("BIF"), BMD("BMD"), BND("BND"), BOB("BOB"), BOV("BOV"), BRL("BRL"), BSD("BSD"), BTN("BTN"), BWP("BWP"), 12 | BYN("BYN"), BYR("BYR"), BZD("BZD"), CAD("CAD"), CDF("CDF"), CHE("CHE"), CHF("CHF"), CHW("CHW"), CLF("CLF"), CLP("CLP"), CNY("CNY"), COP("COP"), 13 | COU("COU"), CRC("CRC"), CUC("CUC"), CUP("CUP"), CVE("CVE"), CZK("CZK"), DJF("DJF"), DKK("DKK"), DOP("DOP"), DZD("DZD"), EGP("EGP"), ERN("ERN"), 14 | ETB("ETB"), EUR("EUR"), FJD("FJD"), FKP("FKP"), GBP("GBP"), GEL("GEL"), GHS("GHS"), GIP("GIP"), GMD("GMD"), GNF("GNF"), GTQ("GTQ"), GYD("GYD"), 15 | HKD("HKD"), HNL("HNL"), HRK("HRK"), HTG("HTG"), HUF("HUF"), IDR("IDR"), ILS("ILS"), INR("INR"), IQD("IQD"), IRR("IRR"), ISK("ISK"), JMD("JMD"), 16 | JOD("JOD"), JPY("JPY"), KES("KES"), KGS("KGS"), KHR("KHR"), KMF("KMF"), KPW("KPW"), KRW("KRW"), KWD("KWD"), KYD("KYD"), KZT("KZT"), LAK("LAK"), 17 | LBP("LBP"), LKR("LKR"), LRD("LRD"), LSL("LSL"), LYD("LYD"), MAD("MAD"), MDL("MDL"), MGA("MGA"), MKD("MKD"), MMK("MMK"), MNT("MNT"), MOP("MOP"), 18 | MRO("MRO"), MUR("MUR"), MVR("MVR"), MWK("MWK"), MXN("MXN"), MXV("MXV"), MYR("MYR"), MZN("MZN"), NAD("NAD"), NGN("NGN"), NIO("NIO"), NOK("NOK"), 19 | NPR("NPR"), NZD("NZD"), OMR("OMR"), PAB("PAB"), PEN("PEN"), PGK("PGK"), PHP("PHP"), PKR("PKR"), PLN("PLN"), PYG("PYG"), QAR("QAR"), RON("RON"), 20 | RSD("RSD"), RUB("RUB"), RWF("RWF"), SAR("SAR"), SBD("SBD"), SCR("SCR"), SDG("SDG"), SEK("SEK"), SGD("SGD"), SHP("SHP"), SLL("SLL"), SOS("SOS"), 21 | SRD("SRD"), SSP("SSP"), STD("STD"), SYP("SYP"), SZL("SZL"), THB("THB"), TJS("TJS"), TMT("TMT"), TND("TND"), TOP("TOP"), TRY("TRY"), TTD("TTD"), 22 | TWD("TWD"), TZS("TZS"), UAH("UAH"), UGX("UGX"), USD("USD"), USN("USN"), UYI("UYI"), UYU("UYU"), UZS("UZS"), VEF("VEF"), VND("VND"), VUV("VUV"), 23 | WST("WST"), XAF("XAF"), XAG("XAG"), XAU("XAU"), XBA("XBA"), XBB("XBB"), XBC("XBC"), XBD("XBD"), XCD("XCD"), XDR("XDR"), XFU("XFU"), XOF("XOF"), 24 | XPD("XPD"), XPF("XPF"), XPT("XPT"), XSU("XSU"), XTS("XTS"), XUA("XUA"), XXX("XXX"), YER("YER"), ZAR("ZAR"), ZMW("ZMW"); 25 | 26 | private String iso4217Code = ""; 27 | 28 | CurrencyType(String code) { 29 | this.iso4217Code = code; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return iso4217Code; 35 | } 36 | 37 | public static CurrencyType getValue(String name) { 38 | CurrencyType currencyType = null; 39 | if (!TextUtils.isEmpty(name)) { 40 | for (CurrencyType type : CurrencyType.values()) { 41 | if (type.iso4217Code.equals(name)) { 42 | currencyType = type; 43 | break; 44 | } 45 | } 46 | } 47 | return currencyType; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/IntegrationValidatorDialogRowItem.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.text.Html; 6 | import android.text.method.LinkMovementMethod; 7 | import android.util.AttributeSet; 8 | import android.view.Gravity; 9 | import android.view.LayoutInflater; 10 | import android.widget.Button; 11 | import android.widget.LinearLayout; 12 | import android.view.View; 13 | import android.widget.TextView; 14 | 15 | import io.branch.referral.R; 16 | 17 | public class IntegrationValidatorDialogRowItem extends LinearLayout { 18 | TextView titleText; 19 | TextView testResultSymbol; 20 | Button detailsButton; 21 | String detailsMessage; 22 | String moreInfoLink; 23 | 24 | public IntegrationValidatorDialogRowItem(Context context, AttributeSet attrs) { 25 | super(context, attrs); 26 | View view = LayoutInflater.from(getContext()).inflate( 27 | R.layout.integration_validator_dialog_row_item, null); 28 | this.addView(view); 29 | titleText = view.findViewById(R.id.title_text); 30 | testResultSymbol = view.findViewById(R.id.pass_or_fail_symbol_text); 31 | detailsButton = view.findViewById(R.id.details_button); 32 | detailsButton.setOnClickListener(view1 -> { 33 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 34 | builder.setMessage(detailsMessage + "\n"); 35 | TextView hyperlinkToDocs = new TextView(context); 36 | hyperlinkToDocs.setMovementMethod(LinkMovementMethod.getInstance()); 37 | hyperlinkToDocs.setGravity(Gravity.CENTER_HORIZONTAL); 38 | String link = ""; 39 | hyperlinkToDocs.setText(Html.fromHtml(link)); 40 | builder.setView(hyperlinkToDocs); 41 | builder.setCancelable(false); 42 | builder.setPositiveButton("OK", (dialog, which) -> {}); 43 | AlertDialog alertDialog = builder.create(); 44 | alertDialog.show(); 45 | }); 46 | } 47 | 48 | public IntegrationValidatorDialogRowItem(Context context, AttributeSet attrs, int defStyle) { 49 | super(context, attrs, defStyle); 50 | } 51 | 52 | public void SetTitleText(String title) { 53 | titleText.setText(title); 54 | } 55 | 56 | public void SetDetailsMessage(String detailsMessage) { 57 | this.detailsMessage = detailsMessage; 58 | } 59 | 60 | public void SetMoreInfoLink(String moreInfoLink) { 61 | this.moreInfoLink = moreInfoLink; 62 | } 63 | 64 | public void SetTestResult(boolean didTestPass) { 65 | String result = didTestPass ? IntegrationValidatorConstants.checkmark : IntegrationValidatorConstants.xmark; 66 | testResultSymbol.setText(result); 67 | ToggleDetailsButton(didTestPass); 68 | } 69 | 70 | public void ToggleDetailsButton(boolean didTestPass) { 71 | if (didTestPass) { 72 | detailsButton.setVisibility(INVISIBLE); 73 | } else { 74 | detailsButton.setVisibility(VISIBLE); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/coroutines/DeviceSignals.kt: -------------------------------------------------------------------------------- 1 | package io.branch.coroutines 2 | 3 | import android.content.Context 4 | import android.text.TextUtils 5 | import android.webkit.WebSettings 6 | import android.webkit.WebView 7 | import io.branch.referral.Branch 8 | import io.branch.referral.BranchLogger.e 9 | import io.branch.referral.BranchLogger.v 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.sync.Mutex 12 | import kotlinx.coroutines.sync.withLock 13 | import kotlinx.coroutines.withContext 14 | 15 | val mutex = Mutex() 16 | 17 | /** 18 | * Returns the user agent string on a background thread via static class WebSettings 19 | * This is the default behavior. 20 | * 21 | * Use a mutex to ensure only one is executed at a time. 22 | * Successive calls will return the cached value. 23 | * 24 | * For performance, this is called at the end of the init, or while awaiting init if enqueued prior. 25 | */ 26 | suspend fun getUserAgentAsync(context: Context): String? { 27 | return withContext(Dispatchers.Default) { 28 | mutex.withLock { 29 | var result: String? = null 30 | 31 | if (!TextUtils.isEmpty(Branch._userAgentString)) { 32 | v("UserAgent cached " + Branch._userAgentString) 33 | result = Branch._userAgentString 34 | } 35 | else { 36 | try { 37 | v("Begin getUserAgentAsync " + Thread.currentThread()) 38 | result = WebSettings.getDefaultUserAgent(context) 39 | v("End getUserAgentAsync " + Thread.currentThread() + " " + result) 40 | } 41 | catch (exception: Exception) { 42 | e("Failed to retrieve userAgent string. " + exception.message) 43 | } 44 | } 45 | 46 | result 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Returns the user agent string on the main thread via WebView instance. 53 | * Use when facing errors with the WebSettings static API. 54 | * https://bugs.chromium.org/p/chromium/issues/detail?id=1279562 55 | * https://bugs.chromium.org/p/chromium/issues/detail?id=1271617 56 | * 57 | * 58 | * Because there is only one main thread, this function will only execute one at a time. 59 | * Successive calls will return the cached value. 60 | */ 61 | suspend fun getUserAgentSync(context: Context): String?{ 62 | return withContext(Dispatchers.Main){ 63 | var result: String? = null 64 | 65 | if(!TextUtils.isEmpty(Branch._userAgentString)){ 66 | v("UserAgent cached " + Branch._userAgentString) 67 | result = Branch._userAgentString 68 | } 69 | else { 70 | try { 71 | v("Begin getUserAgentSync " + Thread.currentThread()) 72 | val w = WebView(context) 73 | result = w.settings.userAgentString 74 | w.destroy() 75 | v("End getUserAgentSync " + Thread.currentThread() + " " + result) 76 | } 77 | catch (ex: Exception) { 78 | e("Failed to retrieve userAgent string. " + ex.message) 79 | } 80 | } 81 | 82 | result 83 | } 84 | } -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/validators/LinkingValidatorConstants.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.validators; 2 | 3 | public class LinkingValidatorConstants { 4 | public static final String canonicalURLPromptText = "Please paste in a web link for the $canonical_url"; 5 | public static final String deeplinkPathPromptText = "Please paste in a value for the $deeplink_path"; 6 | public static final String customKeyPromptText = "Please enter your custom key and value for routing"; 7 | public static final String step1ButtonText = "Next"; 8 | public static final String step2ButtonText = " Generate Links for Testing "; 9 | public static final String step3ButtonText = "Done"; 10 | public static final String canonicalUrlKey = "$canonical_url"; 11 | public static final String deeplinkPathKey = "$deeplink_path"; 12 | 13 | public static final String linkingValidatorRow1Title = "Link using App Link"; 14 | public static final String linkingValidatorRow2Title = "Link using URI scheme"; 15 | public static final String linkingValidatorRow3Title = "Web-only link"; 16 | public static final String linkingValidatorRow4Title = "Link with missing data"; 17 | public static final String linkingValidatorRow5Title = "Warm start use case"; 18 | public static final String linkingValidatorRow6Title = "Foreground click use case"; 19 | 20 | public static final String infoButton1Text = "Verifies that Universal Links / App Links are working correctly for your Branch domain"; 21 | public static final String infoButton2Text = "Verifies that URI schemes work correctly for your Branch domain"; 22 | public static final String infoButton3Text = "Verifies that web-only links are handled correctly to take you to the mobile web"; 23 | public static final String infoButton4Text = "Verifies that your app gracefully handles Branch links missing deep link data"; 24 | public static final String infoButton5Text = "Click the button to simulate a deep link click for the warm start use case"; 25 | public static final String infoButton6Text = "Click the button to simulate a deep link click for the foreground use case"; 26 | 27 | public static final String debugButton1Text = "Ensure you’ve entered the correct SHA 256s on the dashboard and added your Branch domains to the Android Manifest"; 28 | public static final String debugButton2Text = "Ensure that you’ve added a unique Branch URI scheme to the dashboard and Android Manifest"; 29 | public static final String debugButton3Text = "Ensure that your code checks for $web-only in the link data, and if it is true routes the user to the mobile web"; 30 | public static final String debugButton4Text = "Ensure that your code gracefully handles missing or invalid deep link data like taking them to the home screen"; 31 | public static final String debugButton5Text = "Ensure that you are initializing Branch inside of onStart() and that the code is called anytime the app enters the foreground"; 32 | public static final String debugButton6Text = "Ensure that you are calling reInit() inside of onNewIntent() after checking if branch_force_new_session is true"; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/BranchCPIDTest.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | import io.branch.referral.Defines.RequestPath; 5 | 6 | import org.json.JSONObject; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Created by --vbajpai on --2019-09-17 at --10:44 for --android-branch-deep-linking-attribution 13 | */ 14 | 15 | @RunWith(AndroidJUnit4.class) 16 | public class BranchCPIDTest extends BranchTest { 17 | @Test 18 | public void testGetLATD() { 19 | initBranchInstance(); 20 | branch.getLastAttributedTouchData(null); 21 | 22 | ServerRequestQueue queue = ServerRequestQueue.getInstance(getTestContext()); 23 | Assert.assertEquals(1, queue.getSize()); 24 | 25 | ServerRequest cpidRequest = queue.peekAt(0); 26 | 27 | Assert.assertEquals(cpidRequest.getRequestPath(), RequestPath.GetLATD.getPath()); 28 | } 29 | 30 | @Test 31 | public void testGetLATDAttributionWindowDefault() { 32 | initBranchInstance(); 33 | PrefHelper prefHelper = PrefHelper.getInstance(getTestContext()); 34 | 35 | // Defaults to ServerRequestGetLATD.defaultAttributionWindow 36 | int defValue = prefHelper.getLATDAttributionWindow(); 37 | Assert.assertEquals(ServerRequestGetLATD.defaultAttributionWindow, defValue); 38 | 39 | // After request with custom attribution window, still defaults to ServerRequestGetLATD.defaultAttributionWindow 40 | branch.getLastAttributedTouchData(null, 80); 41 | int prefValueA = prefHelper.getLATDAttributionWindow(); 42 | Assert.assertEquals(ServerRequestGetLATD.defaultAttributionWindow, prefValueA); 43 | } 44 | 45 | @Test 46 | public void testGetLATDAttributionWindowSetting() { 47 | //setup 48 | initBranchInstance(); 49 | PrefHelper prefHelper = PrefHelper.getInstance(getTestContext()); 50 | 51 | // Defaults to ServerRequestGetLATD.defaultAttributionWindow 52 | int defValue = prefHelper.getLATDAttributionWindow(); 53 | Assert.assertEquals(ServerRequestGetLATD.defaultAttributionWindow, defValue); 54 | 55 | // Setting new attribution window to 10 56 | prefHelper.setLATDAttributionWindow(10); 57 | int newValue = prefHelper.getLATDAttributionWindow(); 58 | Assert.assertEquals(10, newValue); 59 | 60 | // make request, get its post body, check that the attribution window equals 10 61 | branch.getLastAttributedTouchData(null); 62 | ServerRequestQueue queue = ServerRequestQueue.getInstance(getTestContext()); 63 | Assert.assertEquals(1, queue.getSize()); 64 | 65 | ServerRequest latdRequest = queue.peekAt(0); 66 | Assert.assertTrue(latdRequest instanceof ServerRequestGetLATD); 67 | 68 | JSONObject postBody = latdRequest.getPost(); 69 | Assert.assertNotNull(postBody); 70 | JSONObject userData = postBody.optJSONObject(Defines.Jsonkey.UserData.getKey()); 71 | Assert.assertNotNull(userData); 72 | 73 | int atrWind = userData.optInt(Defines.Jsonkey.LATDAttributionWindow.getKey()); 74 | Assert.assertEquals(10, atrWind); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Branch-SDK/doc/allclasses-noframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes 7 | 8 | 9 | 10 | 11 | 12 |

All Classes

13 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /Branch-SDK/src/androidTest/java/io/branch/referral/BranchTestRequestUtil.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.json.JSONObject; 9 | import org.junit.Assert; 10 | import org.junit.runner.RunWith; 11 | 12 | import java.lang.reflect.Field; 13 | 14 | import io.branch.referral.util.BranchEvent; 15 | 16 | /** 17 | * BranchEvent class tests. 18 | */ 19 | @RunWith(AndroidJUnit4.class) 20 | abstract class BranchTestRequestUtil { 21 | private static final String TAG = "BranchEventTestUtil"; 22 | 23 | // can be pretty short because we mock remote interface and don't actually make async calls from the SDK 24 | public static final int TEST_REQUEST_TIMEOUT = 1000; 25 | public static final int TEST_INIT_SESSION_TIMEOUT = 15000; 26 | 27 | // Dig out the variable for isStandardEvent from the BranchEvent object. 28 | protected boolean isStandardEvent(BranchEvent event) throws Exception { 29 | // Use Reflection to find if it is considered a "Standard Event" 30 | Field f = event.getClass().getDeclaredField("isStandardEvent"); //NoSuchFieldException 31 | f.setAccessible(true); 32 | return (boolean) f.get(event); //IllegalAccessException 33 | } 34 | 35 | // Obtain the ServerRequest that is on the queue that matches the BranchEvent to be logged. 36 | protected ServerRequest logEvent(Context context, BranchEvent event) throws Exception { 37 | event.logEvent(context); 38 | ServerRequest queuedEvent = findRequestOnQueue(context, "name", event.getEventName()); 39 | Assert.assertNotNull(queuedEvent); 40 | 41 | return doFinalUpdate(queuedEvent); 42 | } 43 | 44 | protected ServerRequest findRequestOnQueue(Context context, String key, String eventName) throws InterruptedException { 45 | ServerRequestQueue queue = ServerRequestQueue.getInstance(context); 46 | 47 | int wait_remaining = 2000; 48 | final int interval = 50; 49 | 50 | while (wait_remaining > 0) { 51 | if (queue.getSize() > 0) { 52 | int index = queue.getSize() - 1; 53 | 54 | ServerRequest request = queue.peekAt(index); 55 | JSONObject jsonObject = request.getGetParams(); 56 | 57 | String name = jsonObject.optString(key); 58 | if (name.equals(eventName)) { 59 | // Found it. 60 | return request; 61 | } 62 | } 63 | Thread.sleep(interval); 64 | wait_remaining -= interval; 65 | } 66 | 67 | return null; 68 | } 69 | 70 | protected ServerRequest getLastRequestOnQueue(Context context, int minimumQueueSize) { 71 | ServerRequestQueue queue = ServerRequestQueue.getInstance(context); 72 | 73 | int size = queue.getSize(); 74 | if (size >= minimumQueueSize) { 75 | return queue.peekAt(size - 1); 76 | } 77 | 78 | return null; 79 | } 80 | 81 | protected ServerRequest doFinalUpdate(ServerRequest request) { 82 | Log.i("BranchSDK", "doFinalUpdate" + Thread.currentThread().getName()); 83 | request.doFinalUpdateOnBackgroundThread(); 84 | return request; 85 | } 86 | 87 | protected ServerRequest doFinalUpdateOnMainThread(ServerRequest request) { 88 | request.doFinalUpdateOnMainThread(); 89 | return request; 90 | } 91 | } 92 | 93 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogEvent.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import io.branch.indexing.BranchUniversalObject; 14 | 15 | /** 16 | * *

17 | * The server request for logging events. Handles request creation and execution. 18 | *

19 | */ 20 | public class ServerRequestLogEvent extends ServerRequest { 21 | 22 | public ServerRequestLogEvent(Context context, Defines.RequestPath requestPath, final String eventName, 23 | final HashMap topLevelProperties, final JSONObject standardProperties, 24 | final JSONObject customProperties, final List buoList) { 25 | super(context, requestPath); 26 | JSONObject reqBody = new JSONObject(); 27 | try { 28 | reqBody.put(Defines.Jsonkey.Name.getKey(), eventName); 29 | if (customProperties.length() > 0) { 30 | reqBody.put(Defines.Jsonkey.CustomData.getKey(), customProperties); 31 | } 32 | 33 | if (standardProperties.length() > 0) { 34 | reqBody.put(Defines.Jsonkey.EventData.getKey(), standardProperties); 35 | } 36 | 37 | if (topLevelProperties.size() > 0) { 38 | for (Map.Entry entry : topLevelProperties.entrySet()) { 39 | reqBody.put(entry.getKey(), entry.getValue()); 40 | } 41 | } 42 | 43 | if (buoList.size() > 0) { 44 | JSONArray contentItemsArray = new JSONArray(); 45 | reqBody.put(Defines.Jsonkey.ContentItems.getKey(), contentItemsArray); 46 | for (BranchUniversalObject buo : buoList) { 47 | contentItemsArray.put(buo.convertToJson()); 48 | } 49 | } 50 | setPost(reqBody); 51 | } catch (JSONException e) { 52 | BranchLogger.w("Caught JSONException " + e.getMessage()); 53 | } 54 | updateEnvironment(context, reqBody); 55 | } 56 | 57 | @Override 58 | protected void setPost(JSONObject post) throws JSONException { 59 | super.setPost(post); 60 | prefHelper_.loadPartnerParams(post); 61 | } 62 | 63 | @Override 64 | public boolean handleErrors(Context context) { 65 | return false; 66 | } 67 | 68 | @Override 69 | public void onRequestSucceeded(ServerResponse response, Branch branch) { 70 | } 71 | 72 | @Override 73 | public void handleFailure(int statusCode, String causeMsg) { 74 | } 75 | 76 | @Override 77 | public boolean isGetRequest() { 78 | return false; 79 | } 80 | 81 | @Override 82 | public void clearCallbacks() { 83 | } 84 | 85 | @Override 86 | public BRANCH_API_VERSION getBranchRemoteAPIVersion() { 87 | return BRANCH_API_VERSION.V2; //This is a v2 event 88 | } 89 | 90 | @Override 91 | protected boolean shouldUpdateLimitFacebookTracking() { 92 | return true; 93 | } 94 | 95 | @Override 96 | protected boolean shouldAddDMAParams() { 97 | return true; 98 | } 99 | 100 | public boolean shouldRetryOnFail() { 101 | return false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Branch-SDK/doc/io/branch/referral/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.branch.referral 7 | 8 | 9 | 10 | 11 | 12 |

io.branch.referral

13 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | 20 | 24 | 25 | 30 | 31 | 35 | 36 | 37 | 41 | 42 | 47 | 48 | 56 | 57 | 58 | 59 | 63 | 64 | 69 | 70 | 78 | 79 | 80 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Branch-SDK/doc/allclasses-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes 7 | 8 | 9 | 10 | 11 | 12 |

All Classes

13 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Branch-SDK-TestBed/InitializationTestPlan.md: -------------------------------------------------------------------------------- 1 | # Test plan for different (re)initialization scenarios 2 | 3 | This is a test plan enlisting possible initialization scenarios. As CSMs 4 | find bugs arising from unforeseen initialization scenarios, this list should 5 | be updated. Unless noted, initialization is expected to work in all scenarios. 6 | 7 | 8 | ## Assumptions: 9 | * User followed [Android documentation](https://docs.branch.io/apps/android/) 10 | * Application can have multiple activities (launcher activity is called 11 | `SplashActivity` and all others are `NextActivity`) 12 | * Latest Branch SDK version is being used 13 | 14 | 15 | ## Scenario #1: 16 | * `initSession` is only called in `SplashActivity.onStart`. 17 | 18 | 19 | ### 1. Application in background 20 | ``` 21 | Test by opening the app via: 22 | * launcher icon 23 | * recent apps tray (not available in cold start) 24 | * push notification 25 | ``` 26 | #### Cold start: 27 | * Repro: Close app and remove it from the recent apps list 28 | (not 100% guarantee that this will work because manufacturers determine 29 | if application is killed when removed from recents list). Alternatively, 30 | call `adb shell pm clear my.app.package.id` from the command line (this 31 | will also clear cache and stored data). Open app. 32 | * Initialization entry point = `SplashActivity.onCreate` 33 | 34 | 35 | #### Warm start: 36 | * Repro: Close app via the back button. Open app. 37 | * Initialization entry point = `SplashActivity.onCreate` 38 | 39 | #### Hot start: 40 | * Repro: Close app via the home button. Open app. 41 | * Initialization entry point is either `SplashActivity.onStart` or 42 | `BranchActivityLifecycleObserver.onResume` depending on whether `SplashActivity` 43 | or `NextActivity` was in foreground last. However, if app is opened via 44 | a push notification, the Initialization entry point is guaranteed to be 45 | `SplashActivity.onStart`. 46 | 47 | 48 | ### 2. Application in foreground 49 | ``` 50 | Test by opening the app via: 51 | * push notification 52 | ``` 53 | #### Burning hot start: 54 | * Repro: have the app open 55 | * Initialization entry point = either `SplashActivity.onNewIntent` 56 | or `SplashActivity.onStart` depending if user is currently on `SplashActivity` 57 | or `NextActivity` respectively. There is one exception, if the user is on 58 | `NextActivity` but `SplashActivity` is still partially visible, then the 59 | entry point will be `SplashActivity.onNewIntent`. 60 | 61 | There are four possible scenarios the users can find themselves in, 62 | `reInitSession` is either called or not and the user is currently in 63 | `SplashActivity` or in `NextActivity`. Note that documentation gives 64 | instructions to use `reInitSession`, however it's a new instruction, so 65 | it may be overlooked by existing users. When the latter happens, we may 66 | expect an initialization failure: 67 | * `reInitSession` is not used (i.e. not following documentation) 68 | * User is in `SplashActivity` = Branch initialization is guaranteed to FAIL 69 | * User is in `NextActivity` = Branch initialization is expected work _unless_ 70 | `SplashActivity` is still partially visible (e.g. `NextActivity` is 71 | semi-transparent or is not using the full screen) 72 | 73 | 74 | ## Scenario #2: 75 | * `initSession` is called in both `SplashActivity` and `NextActivity` 76 | 77 | ### 1. Application in foreground 78 | ``` 79 | Test by opening the app via push notification launching `NextActivity`: 80 | * when `NextActivity` is currently in foreground 81 | * when `NextActivity` is in backstack (e.g. has been launched before 82 | but user is currently on another `NextActivity`) 83 | * when `NextActivity` is NOT in backstack (e.g. this particular 84 | `NextActivity` has NOT been launched before) 85 | ``` 86 | 87 | #### Burning hot start: 88 | * Repro: have the app open 89 | 90 | Again, there are expected failures when `reInitSession` is NOT used in `NextActivity` 91 | * user is currently in `NextActivity` = guaranteed initialization failure 92 | * `NextActivity` is in backstack BUT partially visible -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchQRCodeCache.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | public class BranchQRCodeCache { 17 | 18 | private final SystemObserver systemObserver_; 19 | private final Context context_; 20 | 21 | public ConcurrentHashMap cache = new ConcurrentHashMap<>(); 22 | 23 | /** 24 | * Get the singleton instance for this class 25 | * 26 | * @return {@link BranchQRCodeCache} instance if already initialised or null 27 | */ 28 | public static BranchQRCodeCache getInstance() { 29 | Branch b = Branch.getInstance(); 30 | if (b == null) return null; 31 | return b.getBranchQRCodeCache(); 32 | } 33 | 34 | BranchQRCodeCache(Context context) { 35 | context_ = context; 36 | systemObserver_ = new BranchQRCodeCache.SystemObserverInstance(); 37 | } 38 | 39 | /** 40 | * Concrete SystemObserver implementation 41 | */ 42 | private class SystemObserverInstance extends SystemObserver { 43 | public SystemObserverInstance() { 44 | super(); 45 | } 46 | } 47 | 48 | /** 49 | * @return the current SystemObserver instance 50 | */ 51 | SystemObserver getSystemObserver() { 52 | return systemObserver_; 53 | } 54 | 55 | //QR Code Caching Functions 56 | public void addQRCodeToCache(JSONObject parameters, byte[] qrCodeData) { 57 | cache.clear(); 58 | try { 59 | parameters.getJSONObject("data").remove(Defines.Jsonkey.CreationTimestamp.getKey()); 60 | cache.put(parameters, qrCodeData); 61 | } catch (JSONException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | public byte[] checkQRCodeCache(JSONObject parameters) { 67 | if (cache.isEmpty()) { 68 | return null; 69 | } 70 | try { 71 | parameters.getJSONObject("data").remove(Defines.Jsonkey.CreationTimestamp.getKey()); 72 | JSONObject cacheParam = (JSONObject) cache.keySet().iterator().next(); 73 | 74 | if (areEqual(parameters, cacheParam)) { 75 | return cache.get(cacheParam); 76 | } else { 77 | return null; 78 | } 79 | } catch (JSONException e) { 80 | e.printStackTrace(); 81 | return null; 82 | } 83 | } 84 | 85 | //Helper Functions 86 | public static boolean areEqual(Object ob1, Object ob2) throws JSONException { 87 | Object obj1Converted = convertJsonElement(ob1); 88 | Object obj2Converted = convertJsonElement(ob2); 89 | return obj1Converted.equals(obj2Converted); 90 | } 91 | 92 | private static Object convertJsonElement(Object elem) throws JSONException { 93 | if (elem instanceof JSONObject) { 94 | JSONObject obj = (JSONObject) elem; 95 | Iterator keys = obj.keys(); 96 | Map jsonMap = new HashMap<>(); 97 | while (keys.hasNext()) { 98 | String key = keys.next(); 99 | jsonMap.put(key, convertJsonElement(obj.get(key))); 100 | } 101 | return jsonMap; 102 | } else if (elem instanceof JSONArray) { 103 | JSONArray arr = (JSONArray) elem; 104 | Set jsonSet = new HashSet<>(); 105 | for (int i = 0; i < arr.length(); i++) { 106 | jsonSet.add(convertJsonElement(arr.get(i))); 107 | } 108 | return jsonSet; 109 | } else { 110 | return elem; 111 | } 112 | } 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /.github/gptdriverrunscript.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # Constants should ideally be set as environment variables for security 6 | readonly API_URL="https://api.mobileboost.io" 7 | readonly API_ORG_KEY="${API_ORG_KEY}" 8 | readonly API_TOKEN="${API_TOKEN:-null}" 9 | readonly TEST_TIMEOUT="${TEST_TIMEOUT:-7200}" 10 | readonly TEST_TAGS="${TEST_TAGS:-}" 11 | 12 | # Function to post data using curl and handle errors 13 | post_data() { 14 | local url=$1 15 | local body=$2 16 | if ! response=$(curl -s -f -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" -d "$body" "$url"); then 17 | echo "Error: Network request failed with error $response" >&2 18 | exit 1 19 | fi 20 | echo "$response" 21 | } 22 | 23 | # Validate inputs 24 | if [[ -z "$1" || -z "$2" ]]; then 25 | echo "Usage: $0 " 26 | exit 1 27 | fi 28 | 29 | # Validate environment variables 30 | if [[ -z "$API_ORG_KEY" ]]; then 31 | echo "Please set API_ORG_KEY to your organization key" 32 | exit 1 33 | fi 34 | 35 | buildFilename="$1" 36 | buildPlatform="$2" 37 | tags=() 38 | 39 | # Check if TEST_TAGS is provided and split into an array 40 | if [[ -n "$TEST_TAGS" ]]; then 41 | IFS=',' read -ra tags <<< "$TEST_TAGS" 42 | fi 43 | 44 | # Upload build file 45 | echo -n "Uploading build from $buildFilename for $buildPlatform: " 46 | if ! uploadedBuildResponse=$(curl -s -f -X POST \ 47 | -H "Authorization: Bearer $API_TOKEN" \ 48 | -H "Content-Type: multipart/form-data" \ 49 | -F "build=@$buildFilename" \ 50 | -F "organisation_key=$API_ORG_KEY" \ 51 | -F "platform=$buildPlatform" \ 52 | -F "metadata={}" \ 53 | "$API_URL/uploadBuild/"); then 54 | echo "Error: Failed to upload build" >&2 55 | exit 1 56 | fi 57 | 58 | # Extract the buildId 59 | if ! buildId=$(jq -r '.buildId' <<< "$uploadedBuildResponse") || [ -z "$buildId" ]; then 60 | echo "Error: Failed to extract build ID from the response" >&2 61 | exit 1 62 | fi 63 | echo "uploaded (ID: $buildId), app link: $(jq -r '.app_link' <<< "$uploadedBuildResponse")" 64 | 65 | # Execute test suite 66 | echo "Executing test suite..." 67 | jsonPayload="{\"organisationId\": \"$API_ORG_KEY\", \"uploadId\": \"$buildId\"" 68 | if [ ${#tags[@]} -gt 0 ]; then 69 | jsonTags=$(printf ',\"%s\"' "${tags[@]}") 70 | jsonTags="[${jsonTags:1}]" 71 | jsonPayload+=", \"tags\": $jsonTags" 72 | fi 73 | jsonPayload+="}" 74 | if ! testSuiteRunId=$(post_data "$API_URL/tests/execute" "$jsonPayload" | jq -r '.test_suite_ids[0]') || [ -z "$testSuiteRunId" ]; then 75 | echo "Error: Test suite execution failed" >&2 76 | exit 1 77 | fi 78 | 79 | # Wait for test suite to finish 80 | echo -n "Waiting for test suite to finish..." 81 | startTime=$(date +%s) 82 | while true; do 83 | if ! testSuiteData=$(curl -s -f "$API_URL/testSuiteRuns/$testSuiteRunId/gh"); then 84 | echo "Error: Failed to retrieve test suite data" >&2 85 | exit 1 86 | fi 87 | testSuiteStatus=$(jq -r '.status' <<< "$testSuiteData") 88 | 89 | if [[ 90 | "$testSuiteStatus" == "completed" 91 | ]]; then 92 | echo "Status is $testSuiteStatus!" >&2 93 | break 94 | fi 95 | 96 | if (( $(date +%s) - startTime >= TEST_TIMEOUT )); then 97 | echo "Timeout exceeded while waiting for test suite to finish." >&2 98 | exit 1 99 | fi 100 | 101 | echo -n "." 102 | sleep 1 103 | done 104 | echo " done!" 105 | 106 | # Write test suite summary to file if available 107 | if [[ -n "$GITHUB_STEP_SUMMARY" && -w "$GITHUB_STEP_SUMMARY" ]]; then 108 | jq -r '.markdown' <<< "$testSuiteData" >> "$GITHUB_STEP_SUMMARY" 109 | echo "Step summary written to $GITHUB_STEP_SUMMARY" 110 | fi 111 | 112 | # Check test suite result 113 | if ! testSuiteResult=$(jq -r '.result' <<< "$testSuiteData"); then 114 | echo "Test suite did not pass, result: $testSuiteResult" >&2 115 | exit 1 116 | fi 117 | 118 | if [[ "$testSuiteResult" == "succeeded" ]]; then 119 | echo "Test passed successfully" 120 | exit 0 121 | else 122 | echo "Test suite did not pass, result: $testSuiteResult" >&2 123 | exit 1 124 | fi 125 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/BranchPluginSupport.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.DisplayMetrics; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class BranchPluginSupport { 11 | private final SystemObserver systemObserver_; 12 | private final Context context_; 13 | 14 | /** 15 | * Get the singleton instance for this class 16 | * 17 | * @return {@link BranchPluginSupport} instance if already initialised or null 18 | */ 19 | public static BranchPluginSupport getInstance() { 20 | Branch b = Branch.getInstance(); 21 | if (b == null) return null; 22 | return b.getBranchPluginSupport(); 23 | } 24 | 25 | BranchPluginSupport(Context context) { 26 | context_ = context; 27 | systemObserver_ = new BranchPluginSupport.SystemObserverInstance(); 28 | } 29 | 30 | public Map deviceDescription() { 31 | Map deviceDataObj = new HashMap(); 32 | 33 | String osName = SystemObserver.getOS(context_); 34 | if (!isNullOrEmptyOrBlank(osName)) { 35 | deviceDataObj.put(Defines.Jsonkey.OS.getKey(), osName); 36 | } 37 | deviceDataObj.put(Defines.Jsonkey.OSVersionAndroid.getKey(), SystemObserver.getOSVersion()); 38 | 39 | SystemObserver.UniqueId hardwareID = getHardwareID(); 40 | if (!isNullOrEmptyOrBlank(hardwareID.getId())) { 41 | deviceDataObj.put(Defines.Jsonkey.AndroidID.getKey(), hardwareID.getId()); 42 | deviceDataObj.put(Defines.Jsonkey.IsHardwareIDReal.getKey(), hardwareID.isReal()); 43 | } else { 44 | deviceDataObj.put(Defines.Jsonkey.UnidentifiedDevice.getKey(), true); 45 | } 46 | 47 | String countryCode = SystemObserver.getISO2CountryCode(); 48 | if (!TextUtils.isEmpty(countryCode)) { 49 | deviceDataObj.put(Defines.Jsonkey.Country.getKey(), countryCode); 50 | } 51 | 52 | String languageCode = SystemObserver.getISO2LanguageCode(); 53 | if (!TextUtils.isEmpty(languageCode)) { 54 | deviceDataObj.put(Defines.Jsonkey.Language.getKey(), languageCode); 55 | } 56 | 57 | String localIpAddr = SystemObserver.getLocalIPAddress(); 58 | if ((!TextUtils.isEmpty(localIpAddr))) { 59 | deviceDataObj.put(Defines.Jsonkey.LocalIP.getKey(), localIpAddr); 60 | } 61 | 62 | String brandName = SystemObserver.getPhoneBrand(); 63 | if (!isNullOrEmptyOrBlank(brandName)) { 64 | deviceDataObj.put(Defines.Jsonkey.Brand.getKey(), brandName); 65 | } 66 | 67 | String appVersion = SystemObserver.getAppVersion(context_); 68 | deviceDataObj.put(Defines.Jsonkey.AppVersion.getKey(), appVersion); 69 | 70 | String modelName = SystemObserver.getPhoneModel(); 71 | if (!isNullOrEmptyOrBlank(modelName)) { 72 | deviceDataObj.put(Defines.Jsonkey.Model.getKey(), modelName); 73 | } 74 | 75 | DisplayMetrics displayMetrics = SystemObserver.getScreenDisplay(context_); 76 | deviceDataObj.put(Defines.Jsonkey.ScreenDpi.getKey(), displayMetrics.densityDpi); 77 | deviceDataObj.put(Defines.Jsonkey.ScreenHeight.getKey(), displayMetrics.heightPixels); 78 | deviceDataObj.put(Defines.Jsonkey.ScreenWidth.getKey(), displayMetrics.widthPixels); 79 | 80 | return deviceDataObj; 81 | } 82 | 83 | /** 84 | * @return the device Hardware ID. 85 | * Note that if either Debug is enabled or Fetch has been disabled, then return a "fake" ID. 86 | */ 87 | public SystemObserver.UniqueId getHardwareID() { 88 | return getSystemObserver().getUniqueID(context_, Branch.isDeviceIDFetchDisabled()); 89 | } 90 | 91 | /** 92 | * Concrete SystemObserver implementation 93 | */ 94 | private class SystemObserverInstance extends SystemObserver { 95 | public SystemObserverInstance() { 96 | super(); 97 | } 98 | } 99 | 100 | /** 101 | * @return the current SystemObserver instance 102 | */ 103 | SystemObserver getSystemObserver() { 104 | return systemObserver_; 105 | } 106 | 107 | public static boolean isNullOrEmptyOrBlank(String str) { 108 | return TextUtils.isEmpty(str) || str.equals(SystemObserver.BLANK); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/util/CommerceEvent.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral.util; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Evan Groth on 12/21/16. 11 | */ 12 | public class CommerceEvent { 13 | private Double revenue; 14 | private CurrencyType currencyType; 15 | private String transactionID; 16 | private Double shipping; 17 | private Double tax; 18 | private String coupon; 19 | private String affiliation; 20 | private List products; 21 | 22 | public Double getRevenue() { 23 | return revenue; 24 | } 25 | 26 | public void setRevenue(Double revenue) { 27 | this.revenue = revenue; 28 | } 29 | 30 | public CurrencyType getCurrencyType() { 31 | return currencyType; 32 | } 33 | 34 | public void setCurrencyType(CurrencyType currency) { 35 | this.currencyType = currency; 36 | } 37 | 38 | public String getTransactionID() { 39 | return transactionID; 40 | } 41 | 42 | public void setTransactionID(String transactionID) { 43 | this.transactionID = transactionID; 44 | } 45 | 46 | public Double getShipping() { 47 | return shipping; 48 | } 49 | 50 | public void setShipping(Double shipping) { 51 | this.shipping = shipping; 52 | } 53 | 54 | public Double getTax() { 55 | return tax; 56 | } 57 | 58 | public void setTax(Double tax) { 59 | this.tax = tax; 60 | } 61 | 62 | public String getCoupon() { 63 | return coupon; 64 | } 65 | 66 | public void setCoupon(String coupon) { 67 | this.coupon = coupon; 68 | } 69 | 70 | public String getAffiliation() { 71 | return affiliation; 72 | } 73 | 74 | public void setAffiliation(String affiliation) { 75 | this.affiliation = affiliation; 76 | } 77 | 78 | public void setProducts(List products) { 79 | this.products = products; 80 | } 81 | 82 | public void addProduct(Product product) { 83 | if (this.products == null) { 84 | this.products = new ArrayList<>(); 85 | } 86 | this.products.add(product); 87 | } 88 | 89 | public CommerceEvent() { 90 | } 91 | 92 | public CommerceEvent(Double revenue, CurrencyType currency, String transactionID, Double shipping, Double tax, String coupon, String affiliation, List products) { 93 | this.revenue = revenue; 94 | this.currencyType = currency; 95 | this.transactionID = transactionID; 96 | this.shipping = shipping; 97 | this.tax = tax; 98 | this.coupon = coupon; 99 | this.affiliation = affiliation; 100 | this.products = products; 101 | } 102 | 103 | public CommerceEvent(Double revenue, CurrencyType currency, String transactionID, Double shipping, Double tax, String coupon, String affiliation, Product product) { 104 | this.revenue = revenue; 105 | this.currencyType = currency; 106 | this.transactionID = transactionID; 107 | this.shipping = shipping; 108 | this.tax = tax; 109 | this.coupon = coupon; 110 | this.affiliation = affiliation; 111 | this.products = new ArrayList<>(); 112 | this.products.add(product); 113 | } 114 | 115 | public JSONObject getCommerceJSONObject() { 116 | JSONObject jsonObject = new JSONObject(); 117 | try { 118 | jsonObject.put("revenue", this.revenue); 119 | jsonObject.put("currency", this.currencyType); 120 | jsonObject.put("transaction_id", this.transactionID); 121 | jsonObject.put("shipping", this.shipping); 122 | jsonObject.put("tax", this.tax); 123 | jsonObject.put("coupon", this.coupon); 124 | jsonObject.put("affiliation", this.affiliation); 125 | if ( getProducts() != null ) jsonObject.put("products", getProducts()); 126 | } catch (JSONException e) { 127 | 128 | } 129 | 130 | return jsonObject; 131 | } 132 | 133 | public List getProducts() { 134 | if (this.products == null) { 135 | return null; 136 | } 137 | List products = new ArrayList<>(); 138 | for (Product p : this.products) { 139 | products.add(p.getProductJSONObject()); 140 | } 141 | return products; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /.github/workflows/gptdriverautomation.yaml: -------------------------------------------------------------------------------- 1 | name: GPTDriver Test Suite Automation 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'Release-*' # Trigger for branches starting with "Release-" 7 | 8 | jobs: 9 | BuildAndTestAppOnGPTDriver: # Updated job name 10 | runs-on: ubuntu-latest 11 | steps: 12 | # --- Step 1: Extract version from branch name --- 13 | - name: Extract version from branch name 14 | id: extract_version_step 15 | run: | 16 | BRANCH_NAME="${{ github.ref }}" 17 | # Remove 'refs/heads/' prefix 18 | BRANCH_NAME_WITHOUT_PREFIX="${BRANCH_NAME#refs/heads/}" 19 | # Extract version after "Release-" 20 | VERSION=$(echo "$BRANCH_NAME_WITHOUT_PREFIX" | sed -n 's/^Release-\([0-9]*\.[0-9]*\.[0-9]*\)$/\1/p') 21 | 22 | if [ -z "$VERSION" ]; then 23 | echo "Error: Could not extract version from branch name '$BRANCH_NAME_WITHOUT_PREFIX'. Expected format: Release-X.Y.Z" 24 | exit 1 25 | fi 26 | 27 | echo "Extracted version: $VERSION" 28 | echo "VERSION_STRING=$VERSION" >> $GITHUB_ENV 29 | 30 | # --- Step 2: Checkout the Branch SDK repository --- 31 | - name: Checkout BranchMetrics/android-branch-deep-linking-attribution (SDK) 32 | uses: actions/checkout@v5 33 | with: 34 | repository: BranchMetrics/android-branch-deep-linking-attribution 35 | ref: ${{ github.ref }} # Use the same branch that triggered the workflow 36 | path: ./branch-sdk-repo # Checkout into a subdirectory 37 | 38 | # --- Step 3: Build the Branch SDK AAR --- 39 | - name: Set up JDK for SDK build 40 | uses: actions/setup-java@v5 41 | with: 42 | distribution: 'temurin' 43 | java-version: '17' # Ensure this matches the SDK's build requirements 44 | 45 | - name: Build Branch SDK AAR 46 | run: ./gradlew :Branch-SDK:assembleDebug # Use the specific module command 47 | working-directory: ./branch-sdk-repo # Run Gradle from the SDK's checkout directory 48 | 49 | # --- Step 4: Checkout the BranchLinkSimulatorAndroid repository --- 50 | - name: Checkout BranchMetrics/BranchLinkSimulatorAndroid (App) 51 | uses: actions/checkout@v5 52 | with: 53 | repository: BranchMetrics/BranchLinkSimulatorAndroid 54 | ref: gptdriver/linkingTests # Checkout the specific app branch 55 | path: ./app-repo # Checkout into another subdirectory 56 | 57 | # --- Step 5: Copy the generated AAR to the App's libs directory --- 58 | - name: Copy generated AAR to App's libs directory 59 | run: | 60 | mkdir -p ./app-repo/app/libs # Create libs directory if it doesn't exist 61 | cp ./branch-sdk-repo/Branch-SDK/build/outputs/aar/Branch-SDK-debug.aar ./app-repo/app/libs/branch-sdk-debug.aar 62 | # Adjust the AAR name (Branch-SDK-debug.aar) if the actual output name is different 63 | # The path within the SDK repo might vary based on its build structure 64 | working-directory: ${{ github.workspace }} # Run from the root of the GITHUB_WORKSPACE 65 | 66 | # --- Step 6: Build the BranchLinkSimulatorAndroid App using the local AAR --- 67 | - name: Set up JDK for App build 68 | uses: actions/setup-java@v5 69 | with: 70 | distribution: 'temurin' 71 | java-version: '17' # Ensure this matches the App's build requirements 72 | 73 | - name: Build App with local AAR and pass version 74 | run: ./gradlew build -PversionNameFromCi=${{ env.VERSION_STRING }} 75 | working-directory: ./app-repo # Run Gradle from the App's checkout directory 76 | 77 | # --- Step 7: Echo the location of the generated APK file --- 78 | - name: Echo APK location 79 | run: | 80 | APK_PATH="./app-repo/app/build/outputs/apk/debug/app-debug.apk" 81 | echo "Generated APK location: $APK_PATH" 82 | 83 | # --- Step 8: Upload Build Artifacts --- 84 | - name: Upload Build Artifacts 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: BranchLinkSimulator-App-Debug-Build 88 | path: ./app-repo/app/build/outputs/apk/debug/app-debug.apk 89 | 90 | # --- Step 9: Upload and run tests on GPTDriver service. --- 91 | - name: Run GPTDriver tests 92 | run: | 93 | chmod +x ./branch-sdk-repo/.github/gptdriverrunscript.sh 94 | bash ./branch-sdk-repo/.github/gptdriverrunscript.sh ./app-repo/app/build/outputs/apk/debug/app-debug.apk android 95 | env: 96 | API_ORG_KEY: ${{ secrets.MOBILEBOOST_API_ORG_KEY }} 97 | API_KEY: ${{ secrets.MOBILEBOOST_API_ORG_KEY }} 98 | TEST_TAGS: Release 99 | -------------------------------------------------------------------------------- /Branch-SDK/src/main/java/io/branch/referral/TrackingController.java: -------------------------------------------------------------------------------- 1 | package io.branch.referral; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | 8 | import org.json.JSONObject; 9 | 10 | /** 11 | * Created by sojanpr on 3/7/18. 12 | *

13 | * Class for handling the SDK user data or state tracking features 14 | * If tracking disabled SDK will not track any user data or state. 15 | * SDK will not send any network calls when tracking is disabled 16 | *

17 | */ 18 | 19 | public class TrackingController { 20 | /* Flag for controlling the user data tracking state. If disabled SDK will not track any user data or state. SDK will not send any network calls when tracking is disabled*/ 21 | private boolean trackingDisabled = true; 22 | 23 | TrackingController(Context context) { 24 | updateTrackingState(context); 25 | } 26 | 27 | void disableTracking(Context context, boolean disableTracking, @Nullable Branch.TrackingStateCallback callback) { 28 | BranchLogger.v("disableTracking context: " + context + " disableTracking: " + disableTracking + " callback: " + callback); 29 | // If the tracking state is already set to the desired state, then return instantly 30 | if (trackingDisabled == disableTracking) { 31 | if (callback != null) { 32 | BranchLogger.v("Tracking state is already set to " + disableTracking + ". Returning the same to the callback"); 33 | callback.onTrackingStateChanged(trackingDisabled, Branch.getInstance().getFirstReferringParams(), null); 34 | } 35 | return; 36 | } 37 | 38 | trackingDisabled = disableTracking; 39 | PrefHelper.getInstance(context).setBool(PrefHelper.KEY_TRACKING_STATE, disableTracking); 40 | 41 | if (disableTracking) { 42 | BranchLogger.v("Tracking disabled. Clearing all pending requests"); 43 | onTrackingDisabled(context); 44 | if (callback != null) { 45 | callback.onTrackingStateChanged(true, null, null); 46 | } 47 | } else { 48 | BranchLogger.v("Tracking enabled. Registering app init"); 49 | onTrackingEnabled((referringParams, error) -> { 50 | if (callback != null) { 51 | callback.onTrackingStateChanged(false, referringParams, error); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | boolean isTrackingDisabled() { 58 | return trackingDisabled; 59 | } 60 | 61 | public static boolean isTrackingDisabled(@NonNull Context context) { 62 | return PrefHelper.getInstance(context).getBool(PrefHelper.KEY_TRACKING_STATE); 63 | } 64 | 65 | void updateTrackingState(Context context) { 66 | trackingDisabled = PrefHelper.getInstance(context).getBool(PrefHelper.KEY_TRACKING_STATE); 67 | } 68 | 69 | private void onTrackingDisabled(Context context) { 70 | // Clear all pending requests 71 | Branch.getInstance().clearPendingRequests(); 72 | 73 | // Clear any tracking specific preference items 74 | PrefHelper prefHelper = PrefHelper.getInstance(context); 75 | prefHelper.setSessionID(PrefHelper.NO_STRING_VALUE); 76 | prefHelper.setLinkClickID(PrefHelper.NO_STRING_VALUE); 77 | prefHelper.setLinkClickIdentifier(PrefHelper.NO_STRING_VALUE); 78 | prefHelper.setAppLink(PrefHelper.NO_STRING_VALUE); 79 | prefHelper.setInstallReferrerParams(PrefHelper.NO_STRING_VALUE); 80 | prefHelper.setAppStoreReferrer(PrefHelper.NO_STRING_VALUE); 81 | prefHelper.setAppStoreSource(PrefHelper.NO_STRING_VALUE); 82 | prefHelper.setGoogleSearchInstallIdentifier(PrefHelper.NO_STRING_VALUE); 83 | prefHelper.setInitialReferrer(PrefHelper.NO_STRING_VALUE); 84 | prefHelper.setExternalIntentUri(PrefHelper.NO_STRING_VALUE); 85 | prefHelper.setExternalIntentExtra(PrefHelper.NO_STRING_VALUE); 86 | prefHelper.setSessionParams(PrefHelper.NO_STRING_VALUE); 87 | prefHelper.setAnonID(PrefHelper.NO_STRING_VALUE); 88 | prefHelper.setReferringUrlQueryParameters(new JSONObject()); 89 | Branch.getInstance().clearPartnerParameters(); 90 | } 91 | 92 | private void onTrackingEnabled(Branch.BranchReferralInitListener callback) { 93 | BranchLogger.v("onTrackingEnabled callback: " + callback); 94 | Branch branch = Branch.getInstance(); 95 | if (branch != null) { 96 | branch.registerAppInit(branch.getInstallOrOpenRequest(callback, true), false); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Branch-SDK/doc/io/branch/referral/class-use/Base64.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uses of Class io.branch.referral.Base64 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Uses of Class
io.branch.referral.Base64

73 |
74 |
No usage of io.branch.referral.Base64
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 92 |
93 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Branch-SDK/doc/io/branch/referral/class-use/ApkParser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uses of Class io.branch.referral.ApkParser 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Uses of Class
io.branch.referral.ApkParser

73 |
74 |
No usage of io.branch.referral.ApkParser
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 92 |
93 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Branch-SDK/doc/io/branch/referral/class-use/BuildConfig.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Uses of Class io.branch.referral.BuildConfig 7 | 8 | 9 | 10 | 11 | 12 | 22 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 42 |
43 | 70 | 71 |
72 |

Uses of Class
io.branch.referral.BuildConfig

73 |
74 |
No usage of io.branch.referral.BuildConfig
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 92 |
93 | 120 | 121 | 122 | 123 | --------------------------------------------------------------------------------