diff --git a/.gitmodules b/.gitmodules
index 7debf12bc..12fa86081 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,9 +1,6 @@
[submodule "kits/apteligent-kit"]
path = kits/apteligent-kit
url = git@github.com:mParticle-integrations/mparticle-android-integration-apteligent.git
-[submodule "kits/branch-kit"]
- path = kits/branch-kit
- url = git@github.com:mParticle-integrations/mparticle-android-integration-branch.git
[submodule "kits/button-kit"]
path = kits/button-kit
url = git@github.com:mparticle-integrations/mparticle-android-integration-button.git
diff --git a/.mobsf b/.mobsf
index 3afef2999..959a74975 100644
--- a/.mobsf
+++ b/.mobsf
@@ -8,6 +8,7 @@
- "**/src/test/**"
- "**/example/**"
- "**/*-example/**"
+ - "**/SampleApplication/**"
ignore-rules:
- webview_javascript_interface # Intentional: JS bridge for mParticle WebView SDK; workspace token controls access
@@ -27,3 +28,6 @@
- android_certificate_pinning
- android_ssl_pinning
- accept_self_signed_certificate
+ - android_manifest_well_known_assetlinks # Sample apps use third-party domains (e.g. Branch mp-fortune.app.link)
+ - android_task_hijacking1 # Sample apps; targetSdk in build.gradle
+ - android_task_hijacking2 # Sample apps; targetSdk in build.gradle
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index 9621ccb0b..87f9dad31 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -73,6 +73,7 @@ lint:
- kits/apptentive/apptentive-6/**
- kits/appsflyer/appsflyer-6/**
- kits/apptimize/apptimize-3/**
+ - kits/branch/branch-5/**
- kits/braze/braze-38/**
- kits/clevertap/clevertap-7/**
- kits/comscore/comscore-6/**
diff --git a/kits/apptentive/apptentive-6/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt b/kits/apptentive/apptentive-6/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt
index 94d1bfa42..4926752d0 100644
--- a/kits/apptentive/apptentive-6/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt
+++ b/kits/apptentive/apptentive-6/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt
@@ -22,7 +22,7 @@ internal object CustomDataParser {
try {
if (value != null) parseValueGuarded(value) else null
} catch (e: Exception) {
- Log.e(LogTag("pParticle"), "Unable to parse value: $value")
+ Log.e(LogTag("pParticle"), "Unable to parse custom data value")
value
}
diff --git a/kits/branch-kit b/kits/branch-kit
deleted file mode 160000
index 22f5de5b4..000000000
--- a/kits/branch-kit
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 22f5de5b4cc892cbbd6fa9f65f922eebdd8a47ef
diff --git a/kits/branch/branch-5/README.md b/kits/branch/branch-5/README.md
new file mode 100644
index 000000000..7f7a4c253
--- /dev/null
+++ b/kits/branch/branch-5/README.md
@@ -0,0 +1,23 @@
+## Branch Kit Integration
+
+This repository contains the [Branch](https://www.branch.io/) integration for the [mParticle Android SDK](https://github.com/mParticle/mparticle-android-sdk).
+
+### Adding the integration
+
+1. Add the kit dependency to your app's build.gradle:
+
+ ```groovy
+ dependencies {
+ implementation 'com.mparticle:android-branch-kit:5+'
+ }
+ ```
+2. Follow the mParticle Android SDK [quick-start](https://github.com/mParticle/mparticle-android-sdk), then rebuild and launch your app, and verify that you see `"Branch Metrics detected"` in the output of `adb logcat`.
+3. Reference mParticle's integration docs below to enable the integration.
+
+### Documentation
+
+[Branch integration](https://docs.mparticle.com/integrations/branch-metrics/event/)
+
+### License
+
+[Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)
diff --git a/kits/branch/branch-5/SampleApplication/.gitignore b/kits/branch/branch-5/SampleApplication/.gitignore
new file mode 100644
index 000000000..39fb081a4
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/kits/branch/branch-5/SampleApplication/README.md b/kits/branch/branch-5/SampleApplication/README.md
new file mode 100644
index 000000000..c0077d0ac
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/README.md
@@ -0,0 +1,14 @@
+##mParticle-Branch Example
+
+###Overview
+
+This sample app demonstrates:
+
+• How to use the mParticle and Branch in a simple Android application.
+
+• How to create and share Branch links in your app.
+
+• How to Branch deep links are handled in an app when using branch with mParticle.
+
+• How event tracking is handled in mParticle and Branch.
+
diff --git a/kits/branch/branch-5/SampleApplication/app/.gitignore b/kits/branch/branch-5/SampleApplication/app/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/kits/branch/branch-5/SampleApplication/app/build.gradle b/kits/branch/branch-5/SampleApplication/app/build.gradle
new file mode 100644
index 000000000..4f749ed58
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/build.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdk = 34
+ buildToolsVersion "27.0.3"
+ defaultConfig {
+ applicationId "com.mparticle.branchsample"
+ minSdk = 25
+ targetSdk = 33
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ packagingOptions {
+ exclude 'META-INF/proguard/androidx-annotations.pro'
+ exclude 'META-INF/versions/9/module-info.class'
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.8.0'
+ implementation 'com.android.installreferrer:installreferrer:2.2'
+
+ // Local project dependency
+ api project(':local-mParticle-Branch-kit')
+
+ // Uncomment below to use published version instead
+ // implementation 'com.mparticle:android-branch-kit:5+'
+
+ testImplementation 'junit:junit:4.13.2'
+
+ androidTestImplementation('androidx.test.espresso:espresso-core:3.5.1', {
+ exclude group: 'androidx.test', module: 'runner'
+ })
+}
diff --git a/kits/branch/branch-5/SampleApplication/app/proguard-rules.pro b/kits/branch/branch-5/SampleApplication/app/proguard-rules.pro
new file mode 100644
index 000000000..2d3430033
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/proguard-rules.pro
@@ -0,0 +1,16 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/kits/branch/branch-5/SampleApplication/app/src/androidTest/java/com/mparticle/branchsample/ExampleInstrumentedTest.java b/kits/branch/branch-5/SampleApplication/app/src/androidTest/java/com/mparticle/branchsample/ExampleInstrumentedTest.java
new file mode 100644
index 000000000..6cb710a35
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/androidTest/java/com/mparticle/branchsample/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.mparticle.branchsample;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.mparticle.branchsample", appContext.getPackageName());
+ }
+}
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/AndroidManifest.xml b/kits/branch/branch-5/SampleApplication/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a81275e7e
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/SampleApplication.java b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/SampleApplication.java
new file mode 100644
index 000000000..65b473293
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/SampleApplication.java
@@ -0,0 +1,101 @@
+package com.mparticle.branchsample;
+
+import android.app.Application;
+
+import com.mparticle.AttributionError;
+import com.mparticle.AttributionListener;
+import com.mparticle.AttributionResult;
+import com.mparticle.MParticle;
+import com.mparticle.MParticleOptions;
+
+import org.json.JSONObject;
+
+import io.branch.referral.Branch;
+
+public class SampleApplication extends Application implements AttributionListener {
+ private IBranchEvents branchEventCallback;
+ private static SampleApplication instance;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ instance = this;
+ MParticleOptions options = MParticleOptions.builder(this)
+ .credentials("REPLACE_ME_WITH_API_KEY", "REPLACE_ME_WITH_API_SECRET")
+ .attributionListener(this)
+ //Set an instance of AttributionListener
+ //this instance will continually be called whenever
+ //there is new attribution/deep linking information.
+ //It is essentially equivalent to the Branch SDK's BranchReferralInitListener class
+ //that is passed to the Branch SDK's initSession() API.
+ .logLevel(MParticle.LogLevel.VERBOSE)
+ .build();
+
+ MParticle.start(options);
+ }
+
+ public static void setBranchEventCallback(IBranchEvents branchEventCallback) {
+ instance.branchEventCallback = branchEventCallback;
+ }
+
+
+ /**
+ * A few typical scenarios where this callback would be invoked:
+ *
+ * (1) Base case:
+ * - User does not tap on a link, and then opens the app (either after a fresh install or not)
+ * - This block will be invoked with Branch Metrics' response indicating that this user did not tap on a link.
+ *
+ * (2) Deferred deep link:
+ * - User without the app installed taps on a link
+ * - User is redirected from Branch Metrics to the App Store and installs the app
+ * - User opens the app
+ * - This block will be invoked with Branch Metrics' response containing the details of the link
+ *
+ * (3) Deep link with app installed:
+ * - User with the app already installed taps on a link
+ * - Application opens directly to an Activity via a link click, mParticle forwards the launch URI etc to Branch
+ * - This callback will be invoked with Branch Metrics' response containing the details of the link
+ *
+ * If the user navigates away from the app without killing it, this callback could be invoked several times:
+ * once for the initial launch, and then again each time the user taps on a link to re-open the app.
+ *
+ *
+ * This method is equivalent to {@link io.branch.referral.Branch#initSession(Branch.BranchReferralInitListener)}.
+ * This will return deep links params if the app is opened by a link click otherwise a JSONObject with ""+clicked_branch_link = false".
+ * The below example shows how you can message this to your Activities listening for Branch deep link params.
+ *
+ *
+ * @param attributionResult
+ **/
+ @Override
+ public void onResult(AttributionResult attributionResult) {
+ //this will be invoked for
+ if (attributionResult.getServiceProviderId() == MParticle.ServiceProviders.BRANCH_METRICS) {
+ JSONObject params = attributionResult.getParameters();
+ // The Branch SDK will return a response for all new session/app opens, even
+ // if the user did not click a branch link.
+ // The parameters supported by the Branch SDK are documented here:
+ // https://github.com/BranchMetrics/android-branch-deep-linking#branch-provided-data-parameters-in-initsession-callback
+ // For eg:
+// if (params.optBoolean("+clicked_branch_link", false)) {
+// //handle the Branch link click
+// }
+ if (branchEventCallback != null) {
+ branchEventCallback.onBranchInitialised(params);
+ }
+ }
+ }
+
+ @Override
+ public void onError(AttributionError attributionError) {
+ //if the Branch SDK returns an error, it will be surfaced here.
+ if (branchEventCallback != null) {
+ branchEventCallback.onBranchInitialised(new JSONObject());
+ }
+ }
+
+ public interface IBranchEvents {
+ void onBranchInitialised(JSONObject params);
+ }
+}
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/HomeActivity.java b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/HomeActivity.java
new file mode 100644
index 000000000..ee1e49062
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/HomeActivity.java
@@ -0,0 +1,272 @@
+package com.mparticle.branchsample.activities;
+
+import android.content.Intent;
+import android.os.Bundle;
+import androidx.appcompat.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import com.mparticle.MPEvent;
+import com.mparticle.MParticle;
+import com.mparticle.branchsample.R;
+import com.mparticle.commerce.CommerceEvent;
+import com.mparticle.commerce.Impression;
+import com.mparticle.commerce.Product;
+import com.mparticle.commerce.TransactionAttributes;
+import com.mparticle.identity.IdentityApiRequest;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.branch.indexing.BranchUniversalObject;
+import io.branch.referral.SharingHelper;
+import io.branch.referral.util.BranchContentSchema;
+import io.branch.referral.util.ContentMetadata;
+import io.branch.referral.util.CurrencyType;
+import io.branch.referral.util.LinkProperties;
+import io.branch.referral.util.ProductCategory;
+import io.branch.referral.util.ShareSheetStyle;
+
+public class HomeActivity extends AppCompatActivity {
+ public static final String BRANCH_PARAMS = "branch_params";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.home_activity);
+ ((ToggleButton) findViewById(R.id.tracking_cntrl_btn)).setChecked(MParticle.getInstance().getOptOut());
+
+ findViewById(R.id.cmdSetIdentity).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ MParticle.getInstance().Identity().login(IdentityApiRequest.withEmptyUser()
+ .email("foo@example.com")
+ .customerId("12332424555")
+ .build());
+ }
+ });
+
+ findViewById(R.id.cmdLogout).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ MParticle.getInstance().Identity().logout();
+ }
+ });
+
+ findViewById(R.id.cmdTrackView).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ logScreen();
+ }
+ });
+
+ findViewById(R.id.cmdLogSimpleEvent).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ logSimpleEvent();
+ }
+ });
+
+ findViewById(R.id.cmdShareBtn).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ shareBranchLink();
+ }
+ });
+
+ findViewById(R.id.cmdTrackEvent).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ logCommerceEvent((String) ((Spinner) findViewById(R.id.event_name_spinner)).getSelectedItem());
+ }
+ });
+
+ ((ToggleButton) findViewById(R.id.tracking_cntrl_btn)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ MParticle.getInstance().setOptOut(isChecked);
+ }
+ });
+
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ ((TextView) findViewById(R.id.deep_link_params_txt)).setText("");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Intent intent = getIntent();
+ if (!TextUtils.isEmpty(intent.getStringExtra(BRANCH_PARAMS))) {
+ String deepLinkParams = getIntent().getStringExtra(BRANCH_PARAMS);
+ try {
+ String deepLikMsg = "Branch Deep Link Params \n\n" + new JSONObject(deepLinkParams).toString(4);
+ ((TextView) findViewById(R.id.deep_link_params_txt)).setText(deepLikMsg);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ intent.removeExtra(BRANCH_PARAMS);
+ setIntent(intent);
+ } else {
+ ((TextView) findViewById(R.id.deep_link_params_txt)).setText("");
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ setIntent(intent);
+ }
+
+ private void logScreen() {
+ Map eventInfo = new HashMap<>(2);
+ eventInfo.put("screen_attr_key1", "screen_attr_val1");
+ eventInfo.put("screen_attr_key2", "screen_attr_val2");
+ MParticle.getInstance().logScreen("SecondActivity", eventInfo);
+ }
+
+ private void logSimpleEvent() {
+ Map eventInfo = new HashMap<>(2);
+ eventInfo.put("custom_attr_key1", "custom_attr_val1");
+ eventInfo.put("custom_attr_key2", "custom_attr_val2");
+
+ MPEvent event = new MPEvent.Builder("Simple Event", MParticle.EventType.Transaction)
+ .duration(100)
+ .info(eventInfo)
+ .category("Food and Beverages")
+ .build();
+ MParticle.getInstance().logEvent(event);
+ }
+
+ private void logCommerceEvent(String eventName) {
+
+ Map customAttr = new HashMap<>(2);
+ customAttr.put("custom_attr_key1", "custom_attr_val1");
+ customAttr.put("custom_attr_key2", "custom_attr_val2");
+
+ Product product1 = new Product.Builder("Prod1", "my_sku", 100.00)
+ .brand("my_prod_brand")
+ .category("my_prod_category")
+ .couponCode("my_coupon_code")
+ .customAttributes(customAttr)
+ .name("my_prod_name")
+ .position(1)
+ .quantity(2.5)
+ .sku("my_sku")
+ .unitPrice(12.5)
+ .variant("my_variant")
+ .quantity(4)
+ .build();
+
+ Product product2 = new Product.Builder("Impression_prod", "my_sku", 100.00)
+ .brand("my_prod_brand")
+ .category("my_prod_category")
+ .couponCode("my_coupon_code")
+ .customAttributes(customAttr)
+ .name("my_prod_name")
+ .position(1)
+ .quantity(2.5)
+ .sku("my_sku")
+ .unitPrice(12.5)
+ .variant("my_variant")
+ .quantity(4)
+ .build();
+
+ Product product3 = new Product.Builder("prod3", "my_sku", 100.00)
+ .brand("my_prod_brand")
+ .category("my_prod_category")
+ .couponCode("my_coupon_code")
+ .customAttributes(customAttr)
+ .name("my_prod_name")
+ .position(1)
+ .quantity(2.5)
+ .sku("my_sku")
+ .unitPrice(12.5)
+ .variant("my_variant")
+ .quantity(4)
+ .build();
+
+ TransactionAttributes attributes = new TransactionAttributes("foo-transaction-id")
+ .setCouponCode("transaction_coupon_code")
+ .setAffiliation("transaction_affiliation")
+ .setId("transaction_id")
+ .setRevenue(13.5)
+ .setShipping(3.5)
+ .setTax(4.5);
+
+ Impression impression = new Impression("Impression", product2);
+
+ CommerceEvent commerceEvent = new CommerceEvent.Builder(eventName, product1)
+ .currency("USD")
+ .customAttributes(customAttr)
+ .transactionAttributes(attributes)
+ .addImpression(impression)
+ .productListName("my_commerce_event_prod_list")
+ .addProduct(product3)
+ .build();
+ MParticle.getInstance().logEvent(commerceEvent);
+ }
+
+ private void shareBranchLink() {
+ BranchUniversalObject buo = new BranchUniversalObject()
+ .setCanonicalIdentifier("item/12345")
+ .setCanonicalUrl("https://branch.io/deepviews")
+ .setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PRIVATE)
+ .setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
+ .setTitle("My Content Title")
+ .setContentDescription("my_product_description1")
+ .setContentImageUrl("https://example.com/mycontent-12345.png")
+ .setContentExpiration(new Date(1573415635000L))
+ .setContentImageUrl("https://test_img_url")
+ .addKeyWord("My_Keyword1")
+ .addKeyWord("My_Keyword2")
+ .setContentMetadata(
+ new ContentMetadata().setProductName("my_product_name1")
+ .setProductBrand("my_prod_Brand1")
+ .setProductVariant("3T")
+ .setProductCategory(ProductCategory.BABY_AND_TODDLER)
+ .setProductCondition(ContentMetadata.CONDITION.EXCELLENT)
+ .setAddress("Street_name1", "city1", "Region1", "Country1", "postal_code")
+ .setLocation(12.07, -97.5)
+ .setSku("1994320302")
+ .setRating(6.0, 5.0, 7.0, 5)
+ .addImageCaptions("my_img_caption1", "my_img_caption_2")
+ .setQuantity(2.0)
+ .setPrice(23.2, CurrencyType.USD)
+ .setContentSchema(BranchContentSchema.COMMERCE_PRODUCT)
+ .addCustomMetadata("Custom_Content_metadata_key1", "Custom_Content_metadata_val1")
+ );
+
+
+ LinkProperties linkProperties = new LinkProperties()
+ .addTag("Tag1")
+ .setChannel("Sharing_Channel_name")
+ .setFeature("my_feature_name")
+ .addControlParameter("$android_deeplink_path", "custom/path/*")
+ .addControlParameter("$ios_url", "http://example.com/ios")
+ .setDuration(100);
+
+ ShareSheetStyle shareSheetStyle = new ShareSheetStyle(this, "My Sharing Message Title", "My Sharing message body")
+ .setCopyUrlStyle(getResources().getDrawable(android.R.drawable.ic_menu_send), "Save this URl", "Link added to clipboard")
+ .setMoreOptionStyle(getResources().getDrawable(android.R.drawable.ic_menu_search), "Show more")
+ .addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
+ .addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
+ .addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
+ .addPreferredSharingOption(SharingHelper.SHARE_WITH.TWITTER)
+ .setAsFullWidthStyle(true)
+ .setSharingTitle("Share With");
+
+ buo.showShareSheet(this, linkProperties, shareSheetStyle, null);
+ }
+
+}
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/SplashActivity.java b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/SplashActivity.java
new file mode 100644
index 000000000..3e9b42f95
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/java/com/mparticle/branchsample/activities/SplashActivity.java
@@ -0,0 +1,81 @@
+package com.mparticle.branchsample.activities;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.mparticle.MPEvent;
+import com.mparticle.MParticle;
+import com.mparticle.branchsample.R;
+import com.mparticle.branchsample.SampleApplication;
+
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by sojanpr on 4/18/18.
+ *
+ * Splash Activity for MParticle-Branch Kit integration
+ *
+ */
+
+public class SplashActivity extends AppCompatActivity implements SampleApplication.IBranchEvents {
+
+ private static final int SPLASH_DELAY = 1500;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.splash_activity);
+ SampleApplication.setBranchEventCallback(this);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+
+
+ }
+ }, SPLASH_DELAY);
+ }
+
+ /**
+ * Note : Branch needs the new intents to be set to the activity if the launch mode is `SingleTask`
+ **/
+ @Override
+ protected void onNewIntent(Intent intent) {
+ this.setIntent(intent);
+ }
+
+ @Override
+ public void onBranchInitialised(JSONObject params) {
+ final Intent intent = new Intent(SplashActivity.this, HomeActivity.class);
+ if (params.optBoolean("+clicked_branch_link")) {
+ Map infoMap = new HashMap<>();
+ infoMap.put("Referred Link", params.optString("~referring_link"));
+
+ MPEvent event = new MPEvent.Builder("Referred Session", MParticle.EventType.UserContent)
+ .duration(300)
+ .info(infoMap)
+ .category("Session").build();
+ MParticle.getInstance().logEvent(event);
+ intent.putExtra(HomeActivity.BRANCH_PARAMS, params.toString());
+ }
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ SplashActivity.this.startActivity(intent);
+ SplashActivity.this.finish();
+ }
+ }, 1500);
+ }
+
+}
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/drawable/app_icon.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/drawable/app_icon.png
new file mode 100644
index 000000000..e1148ad54
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/drawable/app_icon.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/home_activity.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/home_activity.xml
new file mode 100644
index 000000000..8bce436d0
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/home_activity.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/splash_activity.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/splash_activity.xml
new file mode 100644
index 000000000..5bd991afb
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/layout/splash_activity.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/menu/options_menu.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/menu/options_menu.xml
new file mode 100644
index 000000000..888ab3298
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/menu/options_menu.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-hdpi/ic_launcher.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..cde69bccc
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-mdpi/ic_launcher.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..c133a0cbd
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..bfa42f0e7
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..324e72cdd
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..aee44e138
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/arrays.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/arrays.xml
new file mode 100644
index 000000000..ac33a2337
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/arrays.xml
@@ -0,0 +1,15 @@
+
+
+
+ - add_to_cart
+ - remove_from_cart
+ - add_to_wishlist
+ - remove_from_wishlist
+ - checkout
+ - click
+ - view_detail
+ - purchase
+ - refund
+ - checkout_option
+
+
\ No newline at end of file
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/colors.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..3ab3e9cbc
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/dimens.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..47c822467
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/mparticle.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/mparticle.xml
new file mode 100644
index 000000000..60a8a3aba
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/mparticle.xml
@@ -0,0 +1,5 @@
+
+
+ {MP_KEY}
+ {MP_SECRET}
+
\ No newline at end of file
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/strings.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..efd888237
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/strings.xml
@@ -0,0 +1,16 @@
+
+ MParticle-Branch Sample App
+
+
+ Username
+ Password (optional)
+ Sign in
+ Sign in
+ This email address is invalid
+ This password is too short
+ This password is incorrect
+ This field is required
+ "Contacts permissions are needed for providing email
+ completions."
+
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/main/res/values/styles.xml b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..ebd8fb6c8
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/main/res/values/styles.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/kits/branch/branch-5/SampleApplication/app/src/test/java/com/mparticle/branchsample/ExampleUnitTest.java b/kits/branch/branch-5/SampleApplication/app/src/test/java/com/mparticle/branchsample/ExampleUnitTest.java
new file mode 100644
index 000000000..0469d8f9e
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/app/src/test/java/com/mparticle/branchsample/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.mparticle.branchsample;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/kits/branch/branch-5/SampleApplication/build.gradle b/kits/branch/branch-5/SampleApplication/build.gradle
new file mode 100644
index 000000000..603eba414
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/build.gradle
@@ -0,0 +1,32 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url 'https://dl.bintray.com/kotlin/kotlin-dev/'
+ }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.4.2'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ mavenCentral()
+ maven {
+ url 'https://dl.bintray.com/kotlin/kotlin-dev/'
+ }
+ mavenLocal()
+ google()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/kits/branch/branch-5/SampleApplication/gradle.properties b/kits/branch/branch-5/SampleApplication/gradle.properties
new file mode 100644
index 000000000..6874ee634
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.jar b/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..13372aef5
Binary files /dev/null and b/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.properties b/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..5264804ef
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
diff --git a/kits/branch/branch-5/SampleApplication/gradlew b/kits/branch/branch-5/SampleApplication/gradlew
new file mode 100755
index 000000000..5afed789c
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null # nosemgrep
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD # nosemgrep
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then # nosemgrep
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then # nosemgrep
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` # nosemgrep
+ else
+ eval `echo args$i`="\"$arg\"" # nosemgrep
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS # nosemgrep
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/kits/branch/branch-5/SampleApplication/gradlew.bat b/kits/branch/branch-5/SampleApplication/gradlew.bat
new file mode 100644
index 000000000..aec99730b
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kits/branch/branch-5/SampleApplication/settings.gradle b/kits/branch/branch-5/SampleApplication/settings.gradle
new file mode 100644
index 000000000..e03f143dd
--- /dev/null
+++ b/kits/branch/branch-5/SampleApplication/settings.gradle
@@ -0,0 +1,3 @@
+include ':app'
+include ':local-mParticle-Branch-kit'
+project(':local-mParticle-Branch-kit').projectDir = new File(settingsDir, '..')
diff --git a/kits/branch/branch-5/build.gradle b/kits/branch/branch-5/build.gradle
new file mode 100644
index 000000000..e8429b26e
--- /dev/null
+++ b/kits/branch/branch-5/build.gradle
@@ -0,0 +1,61 @@
+buildscript {
+ ext.kotlin_version = '2.0.20'
+ if (!project.hasProperty('version') || project.version.equals('unspecified')) {
+ project.version = '+'
+ }
+
+ repositories {
+ google()
+ mavenLocal()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:8.1.4'
+ classpath 'com.mparticle:android-kit-plugin:' + project.version
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+plugins {
+ id "org.sonarqube" version "3.5.0.2730"
+ id "org.jlleitschuh.gradle.ktlint" version "13.0.0"
+}
+
+sonarqube {
+ properties {
+ property "sonar.projectKey", "mparticle-android-integration-branchmetrics"
+ property "sonar.organization", "mparticle"
+ property "sonar.host.url", "https://sonarcloud.io"
+ }
+}
+
+apply plugin: 'org.jlleitschuh.gradle.ktlint'
+apply plugin: 'kotlin-android'
+apply plugin: 'com.mparticle.kit'
+
+android {
+ namespace 'com.mparticle.kits.branch'
+ buildFeatures {
+ buildConfig = true
+ }
+ defaultConfig {
+ minSdkVersion 21
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = '17'
+ }
+ testOptions {
+ unitTests.all {
+ jvmArgs += ['--add-opens', 'java.base/java.lang=ALL-UNNAMED']
+ }
+ }
+}
+
+dependencies {
+ api 'io.branch.sdk.android:library:5.12.0'
+}
diff --git a/kits/branch/branch-5/consumer-proguard.pro b/kits/branch/branch-5/consumer-proguard.pro
new file mode 100644
index 000000000..927eace93
--- /dev/null
+++ b/kits/branch/branch-5/consumer-proguard.pro
@@ -0,0 +1,3 @@
+# These are the proguard rules specified by the Branch Metrics SDK's documentation
+
+-keep class com.google.android.gms.ads.identifier.** { *; }
\ No newline at end of file
diff --git a/kits/branch/branch-5/gradle.properties b/kits/branch/branch-5/gradle.properties
new file mode 100644
index 000000000..edb1202c3
--- /dev/null
+++ b/kits/branch/branch-5/gradle.properties
@@ -0,0 +1,4 @@
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.daemon=true
+org.gradle.jvmargs=-Xmx2560m
\ No newline at end of file
diff --git a/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.jar b/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..41d9927a4
Binary files /dev/null and b/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.properties b/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..e1bef7e87
--- /dev/null
+++ b/kits/branch/branch-5/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/kits/branch/branch-5/gradlew b/kits/branch/branch-5/gradlew
new file mode 100755
index 000000000..1b6c78733
--- /dev/null
+++ b/kits/branch/branch-5/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/kits/branch/branch-5/gradlew.bat b/kits/branch/branch-5/gradlew.bat
new file mode 100644
index 000000000..107acd32c
--- /dev/null
+++ b/kits/branch/branch-5/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kits/branch/branch-5/settings.gradle.kts b/kits/branch/branch-5/settings.gradle.kts
new file mode 100644
index 000000000..2ee6c1cf4
--- /dev/null
+++ b/kits/branch/branch-5/settings.gradle.kts
@@ -0,0 +1,2 @@
+rootProject.name = "android-branch-kit"
+include(":")
diff --git a/kits/branch/branch-5/src/main/AndroidManifest.xml b/kits/branch/branch-5/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..61363294b
--- /dev/null
+++ b/kits/branch/branch-5/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchMetricsKit.kt b/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchMetricsKit.kt
new file mode 100644
index 000000000..12e3300f0
--- /dev/null
+++ b/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchMetricsKit.kt
@@ -0,0 +1,289 @@
+package com.mparticle.kits
+
+import android.content.Context
+import android.content.Intent
+import com.mparticle.AttributionError
+import com.mparticle.AttributionResult
+import com.mparticle.MPEvent
+import com.mparticle.MParticle
+import com.mparticle.MParticle.IdentityType
+import com.mparticle.commerce.CommerceEvent
+import com.mparticle.identity.MParticleUser
+import com.mparticle.internal.Logger
+import com.mparticle.kits.KitIntegration.ApplicationStateListener
+import com.mparticle.kits.KitIntegration.AttributeListener
+import com.mparticle.kits.KitIntegration.CommerceListener
+import com.mparticle.kits.KitIntegration.EventListener
+import com.mparticle.kits.KitIntegration.IdentityListener
+import io.branch.referral.Branch
+import io.branch.referral.Branch.BranchReferralInitListener
+import io.branch.referral.BranchError
+import io.branch.referral.BranchLogger
+import io.branch.referral.util.BranchEvent
+import org.json.JSONObject
+import java.math.BigDecimal
+import java.util.HashMap
+import java.util.LinkedList
+
+/**
+ *
+ *
+ * Embedded implementation of the Branch Metrics SDK
+ *
+ *
+ */
+class BranchMetricsKit :
+ KitIntegration(),
+ EventListener,
+ CommerceListener,
+ AttributeListener,
+ ApplicationStateListener,
+ IdentityListener,
+ BranchReferralInitListener {
+ private val branchAppKey = "branchKey"
+ private var isMpidIdentityType = false
+ var identityType: IdentityType? = IdentityType.CustomerId
+ private var mSendScreenEvents = false
+ private var branchUtil: BranchUtil? = null
+
+ override fun getInstance(): Branch? = branch
+
+ override fun getName(): String = NAME
+
+ override fun onKitCreate(
+ settings: Map,
+ context: Context,
+ ): List {
+ branchUtil = BranchUtil()
+ Branch.registerPlugin(
+ "mParticle",
+ javaClass.getPackage()?.specificationVersion ?: "0",
+ )
+ getSettings()[branchAppKey]
+ ?.let { Branch.getAutoInstance(getContext().applicationContext, it) }
+ Branch.sessionBuilder(null).withCallback(this).init()
+ if (Logger.getMinLogLevel() != MParticle.LogLevel.NONE) {
+ Branch.enableLogging()
+ }
+ val sendScreenEvents = settings[FORWARD_SCREEN_VIEWS]
+ mSendScreenEvents =
+ sendScreenEvents != null &&
+ sendScreenEvents.equals("true", ignoreCase = true)
+ setIdentityType(settings)
+ return emptyList()
+ }
+
+ fun setIdentityType(settings: Map) {
+ val userIdentificationType = settings[USER_IDENTIFICATION_TYPE]
+
+ userIdentificationType?.let {
+ if (!KitUtils.isEmpty(it)) {
+ when (it) {
+ "MPID" -> isMpidIdentityType = true
+ "Email" -> identityType = null
+ else -> identityType = IdentityType.valueOf(it)
+ }
+ }
+ }
+ }
+
+ override fun setOptOut(b: Boolean): List {
+ branch?.disableTracking(b)
+ val messages = LinkedList()
+ messages.add(
+ ReportingMessage(
+ this,
+ ReportingMessage.MessageType.OPT_OUT,
+ System.currentTimeMillis(),
+ null,
+ ),
+ )
+ return messages
+ }
+
+ override fun leaveBreadcrumb(s: String): List = emptyList()
+
+ override fun logError(
+ s: String,
+ map: Map,
+ ): List = emptyList()
+
+ override fun logException(
+ e: Exception,
+ map: Map,
+ s: String,
+ ): List = emptyList()
+
+ override fun logEvent(event: MPEvent): List {
+ branchUtil?.createBranchEventFromMPEvent(event)?.logEvent(context)
+ val messages = LinkedList()
+ messages.add(ReportingMessage.fromEvent(this, event))
+ return messages
+ }
+
+ override fun logLtvIncrease(
+ bigDecimal: BigDecimal,
+ bigDecimal1: BigDecimal,
+ s: String,
+ map: Map,
+ ): List = emptyList()
+
+ override fun logEvent(commerceEvent: CommerceEvent): List {
+ branchUtil?.createBranchEventFromMPCommerceEvent(commerceEvent)?.logEvent(context)
+ val messages: MutableList = LinkedList()
+ messages.add(ReportingMessage.fromEvent(this, commerceEvent))
+ return messages
+ }
+
+ override fun logScreen(
+ screenName: String,
+ eventAttributes: Map,
+ ): List {
+ var eventAttributes: Map? = eventAttributes
+ return if (mSendScreenEvents) {
+ val logScreenEvent = BranchEvent(screenName)
+ if (eventAttributes == null) {
+ eventAttributes = HashMap()
+ }
+ branchUtil?.updateBranchEventWithCustomData(logScreenEvent, eventAttributes)
+ logScreenEvent.logEvent(context)
+ val messages =
+ LinkedList()
+ messages.add(
+ ReportingMessage(
+ this,
+ ReportingMessage.MessageType.SCREEN_VIEW,
+ System.currentTimeMillis(),
+ eventAttributes,
+ ),
+ )
+ messages
+ } else {
+ emptyList()
+ }
+ }
+
+ private val branch: Branch?
+ get() = settings[branchAppKey]?.let { Branch.getInstance() }
+
+ override fun setInstallReferrer(intent: Intent) {
+ BranchLogger.w(
+ "setInstallReferrer(intent) was ignored, INSTALL_REFERRER broadcast intent is deprecated, relevant data is now collected automatically using the Play Install Referrer Library bundled together with Branch SDK.",
+ )
+ }
+
+ override fun setUserAttribute(
+ s: String,
+ s1: String,
+ ) {}
+
+ override fun setUserAttributeList(
+ s: String,
+ list: List,
+ ) {}
+
+ override fun supportsAttributeLists(): Boolean = true
+
+ override fun setAllUserAttributes(
+ map: Map,
+ map1: Map>,
+ ) {}
+
+ override fun removeUserAttribute(s: String) {}
+
+ override fun setUserIdentity(
+ identityType: IdentityType,
+ s: String,
+ ) {}
+
+ override fun removeUserIdentity(identityType: IdentityType) {}
+
+ override fun logout(): List {
+ branch?.logout()
+ val messageList: MutableList = LinkedList()
+ messageList.add(ReportingMessage.logoutMessage(this))
+ return messageList
+ }
+
+ override fun onIdentifyCompleted(
+ mParticleUser: MParticleUser,
+ filteredIdentityApiRequest: FilteredIdentityApiRequest,
+ ) {
+ updateUser(mParticleUser)
+ }
+
+ override fun onLoginCompleted(
+ mParticleUser: MParticleUser,
+ filteredIdentityApiRequest: FilteredIdentityApiRequest,
+ ) {
+ updateUser(mParticleUser)
+ }
+
+ override fun onLogoutCompleted(
+ mParticleUser: MParticleUser,
+ filteredIdentityApiRequest: FilteredIdentityApiRequest,
+ ) {
+ updateUser(mParticleUser)
+ }
+
+ override fun onModifyCompleted(
+ mParticleUser: MParticleUser,
+ filteredIdentityApiRequest: FilteredIdentityApiRequest,
+ ) {
+ updateUser(mParticleUser)
+ }
+
+ override fun onUserIdentified(mParticleUser: MParticleUser) {}
+
+ private fun updateUser(mParticleUser: MParticleUser) {
+ var identity: String? = null
+ if (isMpidIdentityType) {
+ identity = mParticleUser.id.toString()
+ } else if (identityType != null) {
+ val mPIdentity = mParticleUser.userIdentities[identityType]
+ if (mPIdentity != null) {
+ identity = mPIdentity
+ }
+ }
+ if (identity != null) {
+ branch?.setIdentity(identity)
+ }
+ }
+
+ /**
+ * Don't do anything here - we make the call to get the latest deep link info during onResume, below.
+ */
+ override fun onInitFinished(
+ jsonResult: JSONObject?,
+ branchError: BranchError?,
+ ) {
+ if (jsonResult != null && jsonResult.length() > 0) {
+ val result =
+ AttributionResult()
+ .setParameters(jsonResult)
+ .setServiceProviderId(this.configuration.kitId)
+ kitManager.onResult(result)
+ }
+ if (branchError != null) {
+ if (branchError.errorCode != BranchError.ERR_BRANCH_ALREADY_INITIALIZED) {
+ val error =
+ AttributionError()
+ .setMessage(branchError.toString())
+ .setServiceProviderId(this.configuration.kitId)
+ kitManager.onError(error)
+ }
+ }
+ }
+
+ override fun onApplicationForeground() {
+ Branch.sessionBuilder(null).withCallback(this).init()
+ }
+
+ override fun onApplicationBackground() {}
+
+ companion object {
+ private const val FORWARD_SCREEN_VIEWS = "forwardScreenViews"
+ private const val USER_IDENTIFICATION_TYPE = "userIdentificationType"
+ const val NAME = "Branch Metrics"
+ }
+}
diff --git a/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchUtil.kt b/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchUtil.kt
new file mode 100644
index 000000000..2a0816971
--- /dev/null
+++ b/kits/branch/branch-5/src/main/kotlin/com/mparticle/kits/BranchUtil.kt
@@ -0,0 +1,342 @@
+package com.mparticle.kits
+
+import android.text.TextUtils
+import com.mparticle.MPEvent
+import com.mparticle.commerce.CommerceEvent
+import com.mparticle.commerce.Product
+import com.mparticle.commerce.Promotion
+import com.mparticle.commerce.TransactionAttributes
+import io.branch.indexing.BranchUniversalObject
+import io.branch.referral.Defines
+import io.branch.referral.util.BRANCH_STANDARD_EVENT
+import io.branch.referral.util.BranchEvent
+import io.branch.referral.util.CurrencyType
+import io.branch.referral.util.ProductCategory
+
+/**
+ * Created by sojanpr on 4/11/18.
+ *
+ *
+ * Class for Branch utility methods to convert MParticle events to Branch events
+ *
+ */
+internal class BranchUtil {
+ internal enum class MPEventKeys {
+ Position,
+ Amount,
+ ScreenName,
+ Impression,
+ ProductListName,
+ ProductListSource,
+ CheckoutOptions,
+ CheckoutStep,
+ }
+
+ internal enum class ExtraBranchEventKeys {
+ ProductCategory,
+ }
+
+ private val branchMParticleEventNames = HashMap()
+ private var branchMParticlePromotionEventNames = HashMap()
+
+ private fun createBranchEventFromEventName(eventName: String?): BranchEvent {
+ val branchEvent: BranchEvent
+ val branchStandardEvent = branchMParticleEventNames[eventName]
+ branchEvent = branchStandardEvent?.let { BranchEvent(it) } ?: BranchEvent(eventName)
+ branchEvent.setDescription(eventName)
+ return branchEvent
+ }
+
+ private fun createBranchEventFromPromotionEventName(eventName: String?): BranchEvent {
+ val branchEvent: BranchEvent
+ val branchStandardEvent = branchMParticlePromotionEventNames[eventName]
+ branchEvent = branchStandardEvent?.let { BranchEvent(it) } ?: BranchEvent(eventName)
+ branchEvent.setDescription(eventName)
+ return branchEvent
+ }
+
+ internal class MapReader(
+ mapObject: Map?,
+ ) {
+ private lateinit var mapObj: MutableMap
+
+ fun readOutString(key: String): String? = mapObj.remove(key)
+
+ fun readOutDouble(key: String): Double? {
+ var value: Double? = null
+ try {
+ value = mapObj[key]?.toDouble()
+ mapObj.remove(key)
+ } catch (ignore: Exception) {
+ }
+ return value
+ }
+
+ val map: Map
+ get() = mapObj
+
+ init {
+
+ if (!mapObject.isNullOrEmpty()) {
+ this.mapObj = HashMap(mapObject)
+ }
+ }
+ }
+
+ // Region Translate MPEvents
+ fun createBranchEventFromMPEvent(mpEvent: MPEvent): BranchEvent {
+ val branchEvent = BranchEvent(mpEvent.eventName)
+ branchEvent.setDescription(mpEvent.eventName)
+ val buo = BranchUniversalObject()
+ branchEvent.addContentItems(buo)
+ // Apply event category
+ if (!TextUtils.isEmpty(mpEvent.category)) {
+ translateEventCategory(buo, mpEvent.category)
+ }
+ // Apply event name
+ if (!TextUtils.isEmpty(mpEvent.eventName)) {
+ buo.title = mpEvent.eventName
+ }
+ if (mpEvent.customAttributeStrings != null) {
+ updateEventWithInfo(branchEvent, mpEvent.customAttributeStrings)
+ }
+ return branchEvent
+ }
+
+ private fun updateEventWithInfo(
+ event: BranchEvent,
+ info: Map?,
+ ) {
+ val mapReader = MapReader(info)
+ updateBranchEventWithCustomData(event, mapReader.map)
+ }
+
+ // End Region Translate MPEvents
+ // Region Translate CommerceEvents
+ fun createBranchEventFromMPCommerceEvent(event: CommerceEvent): BranchEvent {
+ val branchEvent: BranchEvent =
+ if (event.productAction != null) {
+ createBranchEventFromEventName(event.productAction)
+ } else if (event.promotionAction != null) {
+ createBranchEventFromPromotionEventName(event.promotionAction)
+ } else {
+ createBranchEventFromEventName(MPEventKeys.Impression.name)
+ }
+ // Add all the products in the product list to Branch event
+ if (event.products != null) {
+ val additionalMetadata = HashMap()
+ if (!TextUtils.isEmpty(event.productListName)) {
+ additionalMetadata[MPEventKeys.ProductListName.name] = event.productListName
+ }
+ if (!TextUtils.isEmpty(event.productListSource)) {
+ additionalMetadata[MPEventKeys.ProductListSource.name] = event.productListSource
+ }
+ addProductListToBranchEvent(branchEvent, event.products, event, additionalMetadata)
+ }
+
+ // Add all impressions to the Branch Event
+ event.impressions?.let {
+ for (impression in it) {
+ val additionalMetadata = HashMap()
+ if (!TextUtils.isEmpty(impression.listName)) {
+ additionalMetadata[MPEventKeys.Impression.name] = impression.listName
+ }
+ addProductListToBranchEvent(
+ branchEvent,
+ impression.products,
+ event,
+ additionalMetadata,
+ )
+ }
+ }
+ event.transactionAttributes?.let {
+ updateBranchEventWithTransactionAttributes(branchEvent, it)
+ }
+ event.customAttributeStrings?.let {
+ updateBranchEventWithCustomData(branchEvent, it)
+ }
+ if (!TextUtils.isEmpty(event.productListName)) {
+ branchEvent.addCustomDataProperty(
+ MPEventKeys.ProductListName.name,
+ event.productListName,
+ )
+ }
+ if (!TextUtils.isEmpty(event.productListSource)) {
+ branchEvent.addCustomDataProperty(
+ MPEventKeys.ProductListSource.name,
+ event.productListSource,
+ )
+ }
+ if (!TextUtils.isEmpty(event.checkoutOptions)) {
+ branchEvent.addCustomDataProperty(
+ MPEventKeys.CheckoutOptions.name,
+ event.checkoutOptions,
+ )
+ }
+ if (!TextUtils.isEmpty(event.screen)) {
+ branchEvent.addCustomDataProperty(MPEventKeys.ScreenName.name, event.screen)
+ }
+ if (event.checkoutStep != null) {
+ try {
+ branchEvent.addCustomDataProperty(
+ MPEventKeys.CheckoutStep.name,
+ event.checkoutStep.toString(),
+ )
+ } catch (ignore: Exception) {
+ }
+ }
+ if (!TextUtils.isEmpty(event.currency)) {
+ branchEvent.setCurrency(CurrencyType.getValue(event.currency))
+ }
+ return branchEvent
+ }
+
+ private fun addProductListToBranchEvent(
+ branchEvent: BranchEvent,
+ products: List?,
+ event: CommerceEvent,
+ additionalMetadata: Map,
+ ) {
+ if (products != null) {
+ for (product in products) {
+ branchEvent.addContentItems(
+ createBranchUniversalObjectFromMProduct(
+ product,
+ event,
+ additionalMetadata,
+ ),
+ )
+ }
+ }
+ }
+
+ private fun createBranchUniversalObjectFromMProduct(
+ product: Product,
+ event: CommerceEvent,
+ additionalMetadata: Map?,
+ ): BranchUniversalObject {
+ val buo = BranchUniversalObject()
+ if (!TextUtils.isEmpty(product.brand)) {
+ buo.contentMetadata.setProductBrand(product.brand)
+ }
+ if (!TextUtils.isEmpty(product.category)) {
+ translateEventCategory(buo, product.category)
+ }
+ if (!TextUtils.isEmpty(product.couponCode)) {
+ buo.contentMetadata.addCustomMetadata(Defines.Jsonkey.Coupon.key, product.couponCode)
+ }
+ if (!TextUtils.isEmpty(product.name)) {
+ buo.contentMetadata.setProductName(product.name)
+ }
+ if (!TextUtils.isEmpty(product.variant)) {
+ buo.contentMetadata.setProductVariant(product.variant)
+ }
+ if (!TextUtils.isEmpty(product.sku)) {
+ buo.contentMetadata.setSku(product.sku)
+ }
+ if (product.position != null) {
+ buo.contentMetadata.addCustomMetadata(
+ MPEventKeys.Position.name,
+ (product.position).toString(),
+ )
+ }
+ buo.contentMetadata.setPrice(product.unitPrice, CurrencyType.getValue(event.currency))
+ buo.contentMetadata.setQuantity(product.quantity)
+ buo.contentMetadata.addCustomMetadata(
+ MPEventKeys.Amount.name,
+ product.totalAmount.toString(),
+ )
+ product.customAttributes?.let {
+ addCustomDataToBranchUniversalObject(buo, it)
+ }
+ additionalMetadata?.let { addCustomDataToBranchUniversalObject(buo, it) }
+ return buo
+ }
+
+ private fun addCustomDataToBranchUniversalObject(
+ buo: BranchUniversalObject,
+ customAttr: Map,
+ ) {
+ val contentMetadata = buo.contentMetadata
+ for (key in customAttr.keys) {
+ contentMetadata.addCustomMetadata(key, customAttr[key])
+ }
+ }
+
+ private fun updateBranchEventWithTransactionAttributes(
+ event: BranchEvent,
+ transAttr: TransactionAttributes,
+ ) {
+ if (!TextUtils.isEmpty(transAttr.affiliation)) {
+ event.setAffiliation(transAttr.affiliation)
+ }
+ if (!TextUtils.isEmpty(transAttr.couponCode)) {
+ event.setCoupon(transAttr.couponCode)
+ }
+ if (!TextUtils.isEmpty(transAttr.id)) {
+ event.setTransactionID(transAttr.id)
+ }
+ transAttr.revenue?.let {
+ event.setRevenue(it)
+ }
+ transAttr.shipping?.let {
+ event.setShipping(it)
+ }
+ transAttr.tax?.let {
+ event.setTax(it)
+ }
+ }
+
+ fun updateBranchEventWithCustomData(
+ branchEvent: BranchEvent,
+ eventAttributes: Map,
+ ) {
+ for (key in eventAttributes.keys) {
+ branchEvent.addCustomDataProperty(key, eventAttributes[key])
+ if (key == "customer_event_alias") {
+ branchEvent.setCustomerEventAlias(eventAttributes[key])
+ }
+ }
+ } // End Region Translate CommerceEvents
+
+ companion object {
+ /**
+ * Translate the given MPEvent / Commerce event to [ProductCategory] and add to the BUO
+ *
+ * @param buo BUO representing content for the event
+ * @param categoryName MPEvent / Commerce event category
+ */
+ private fun translateEventCategory(
+ buo: BranchUniversalObject,
+ categoryName: String?,
+ ) {
+ val category = ProductCategory.getValue(categoryName)
+ if (category != null) {
+ buo.contentMetadata.setProductCategory(category)
+ } else {
+ buo.contentMetadata.addCustomMetadata(
+ ExtraBranchEventKeys.ProductCategory.name,
+ categoryName,
+ )
+ }
+ }
+ }
+
+ init {
+ // Mapping MParticle Commerce Event names to possible matches in Branch events
+ branchMParticleEventNames[Product.ADD_TO_CART] = BRANCH_STANDARD_EVENT.ADD_TO_CART.getName()
+ branchMParticleEventNames[Product.REMOVE_FROM_CART] = "REMOVE_FROM_CART"
+ branchMParticleEventNames[Product.ADD_TO_WISHLIST] = BRANCH_STANDARD_EVENT.ADD_TO_WISHLIST.getName()
+ branchMParticleEventNames[Product.REMOVE_FROM_WISHLIST] = "REMOVE_FROM_WISHLIST"
+ branchMParticleEventNames[Product.CHECKOUT] = BRANCH_STANDARD_EVENT.INITIATE_PURCHASE.getName()
+ branchMParticleEventNames[Product.CLICK] = "CLICK_ITEM"
+ branchMParticleEventNames[Product.PURCHASE] = BRANCH_STANDARD_EVENT.PURCHASE.getName()
+ branchMParticleEventNames[Product.DETAIL] = BRANCH_STANDARD_EVENT.VIEW_ITEM.getName()
+ branchMParticleEventNames[Product.CHECKOUT_OPTION] = "PURCHASE_OPTION"
+ branchMParticleEventNames[Product.REFUND] = "REFUND"
+ branchMParticleEventNames[MPEventKeys.Impression.name] = "IMPRESSION"
+ branchMParticlePromotionEventNames = HashMap()
+ branchMParticlePromotionEventNames[Promotion.VIEW] = "VIEW_PROMOTION"
+ branchMParticlePromotionEventNames[Promotion.CLICK] = "CLICK_PROMOTION"
+ }
+}
diff --git a/kits/branch/branch-5/src/test/kotlin/com/mparticle/kits/BranchKitTests.kt b/kits/branch/branch-5/src/test/kotlin/com/mparticle/kits/BranchKitTests.kt
new file mode 100644
index 000000000..dc92f808d
--- /dev/null
+++ b/kits/branch/branch-5/src/test/kotlin/com/mparticle/kits/BranchKitTests.kt
@@ -0,0 +1,53 @@
+package com.mparticle.kits
+
+import android.content.Context
+import com.mparticle.MParticleOptions
+import org.junit.Assert
+import org.junit.Test
+import org.mockito.Mockito
+
+class BranchKitTests {
+ private val kit: KitIntegration
+ get() = BranchMetricsKit()
+
+ @Test
+ @Throws(Exception::class)
+ fun testGetName() {
+ val name = kit.name
+ Assert.assertTrue(!name.isNullOrEmpty())
+ }
+
+ /**
+ * Kit *should* throw an exception when they're initialized with the wrong settings.
+ *
+ */
+ @Test
+ @Throws(Exception::class)
+ fun testOnKitCreate() {
+ var e: Exception? = null
+ try {
+ val kit = kit
+ val settings = HashMap()
+ settings["fake setting"] = "fake"
+ kit.onKitCreate(settings, Mockito.mock(Context::class.java))
+ } catch (ex: Exception) {
+ e = ex
+ }
+ Assert.assertNotNull(e)
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testClassName() {
+ val options = Mockito.mock(MParticleOptions::class.java)
+ val factory = KitIntegrationFactory(options)
+ val integrations = factory.supportedKits.values
+ val className = kit.javaClass.name
+ for (integration in integrations) {
+ if (integration.name == className) {
+ return
+ }
+ }
+ Assert.fail("$className not found as a known integration.")
+ }
+}
diff --git a/settings-kits.gradle b/settings-kits.gradle
index 289f99d20..c22ac56cf 100644
--- a/settings-kits.gradle
+++ b/settings-kits.gradle
@@ -8,9 +8,9 @@ include (
':kits:apptentive:apptentive-6',
':kits:apptimize:apptimize-3',
':kits:apteligent-kit',
- ':kits:branch-kit',
//blueshift hosts kit
':kits:braze:braze-38',
+ ':kits:branch:branch-5',
':kits:button-kit',
':kits:clevertap:clevertap-7',
':kits:comscore:comscore-6',