diff --git a/.gitignore b/.gitignore index 5588081..c294e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ .cxx local.properties /support/repo/ + +# Sonatype signing +secure_do_not_distribute_sonatype.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d8fd6c..25179e2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.4.0 + +##### Added +* Added support to parse `braze_subscription_groups` in the Identity traits to subscribe and unsubscribe from Braze subscription groups. +* Updated to use Braze Android SDK 26.1.0 and analytics-kotlin 1.13.0. + ## 1.3.0 #### Breaking diff --git a/braze-segment-kotlin/src/androidTest/java/com/segment/analytics/kotlin/destinations/braze/BrazeAndroidTest.kt b/braze-segment-kotlin/src/androidTest/java/com/segment/analytics/kotlin/destinations/braze/BrazeAndroidTest.kt index 63cd47d..b92d9f9 100644 --- a/braze-segment-kotlin/src/androidTest/java/com/segment/analytics/kotlin/destinations/braze/BrazeAndroidTest.kt +++ b/braze-segment-kotlin/src/androidTest/java/com/segment/analytics/kotlin/destinations/braze/BrazeAndroidTest.kt @@ -64,7 +64,8 @@ class BrazeAndroidTest { content = mapOf( "apiKey" to JsonPrimitive(apiKey), "customEndpoint" to JsonPrimitive(customEndpoint), - "automatic_in_app_message_registration_enabled" to JsonPrimitive(autoInAppRegistration) + "automatic_in_app_message_registration_enabled" to JsonPrimitive(autoInAppRegistration), + "logPurchaseWhenRevenuePresent" to JsonPrimitive(false) ) ) val integrations = JsonObject(mapOf("Appboy" to brazeJsonSettings)) diff --git a/braze-segment-kotlin/src/main/java/com/segment/analytics/kotlin/destinations/braze/BrazeDestination.kt b/braze-segment-kotlin/src/main/java/com/segment/analytics/kotlin/destinations/braze/BrazeDestination.kt index 22c0cfd..60179e7 100644 --- a/braze-segment-kotlin/src/main/java/com/segment/analytics/kotlin/destinations/braze/BrazeDestination.kt +++ b/braze-segment-kotlin/src/main/java/com/segment/analytics/kotlin/destinations/braze/BrazeDestination.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Context import androidx.annotation.VisibleForTesting import com.braze.Braze -import com.braze.BrazeUser import com.braze.configuration.BrazeConfig import com.braze.enums.BrazeSdkMetadata import com.braze.enums.Gender @@ -34,7 +33,8 @@ data class BrazeSettings( val apiKey: String, val customEndpoint: String, @SerialName("automatic_in_app_message_registration_enabled") - val automaticInAppMessageRegistrationEnabled: Boolean + val automaticInAppMessageRegistrationEnabled: Boolean, + val logPurchaseWhenRevenuePresent: Boolean ) class BrazeDestination( @@ -51,6 +51,7 @@ class BrazeDestination( get() = brazeTestingMock ?: Braze.getInstance(context) private var isAutomaticInAppMessageRegistrationEnabled: Boolean = false + private var shouldLogPurchaseWhenRevenuePresent: Boolean = true override fun update(settings: Settings, type: Plugin.UpdateType) { super.update(settings, type) @@ -74,6 +75,7 @@ class BrazeDestination( } Braze.configure(context, builder.build()) isAutomaticInAppMessageRegistrationEnabled = brazeSettings.automaticInAppMessageRegistrationEnabled + shouldLogPurchaseWhenRevenuePresent = brazeSettings.logPurchaseWhenRevenuePresent analytics.log("Braze Destination loaded") } @@ -92,16 +94,17 @@ class BrazeDestination( eventName == INSTALL_EVENT_NAME -> { try { val campaignProps: JsonObject? = properties["campaign"]?.safeJsonObject - val currentUser = braze.currentUser - if (campaignProps != null && currentUser != null) { - currentUser.setAttributionData( - AttributionData( - campaignProps.getString("source").orEmpty(), - campaignProps.getString("name").orEmpty(), - campaignProps.getString("ad_group").orEmpty(), - campaignProps.getString("ad_creative").orEmpty() + braze.getCurrentUser { currentUser -> + if (campaignProps != null) { + currentUser.setAttributionData( + AttributionData( + campaignProps.getString("source").orEmpty(), + campaignProps.getString("name").orEmpty(), + campaignProps.getString("ad_group").orEmpty(), + campaignProps.getString("ad_creative").orEmpty() + ) ) - ) + } } return payload } catch (exception: Exception) { @@ -111,7 +114,7 @@ class BrazeDestination( ) } } - revenue != 0.0 || eventName == PURCHASE_EVENT_NAME_1 || eventName == PURCHASE_EVENT_NAME_2 -> { + shouldLogPurchaseWhenRevenuePresent && revenue != 0.0 || eventName == PURCHASE_EVENT_NAME_1 || eventName == PURCHASE_EVENT_NAME_2 -> { val currencyCode: String? = if (properties.getString(CURRENCY_KEY).isNullOrBlank()) { DEFAULT_CURRENCY_CODE @@ -165,140 +168,164 @@ class BrazeDestination( } val traits: Traits = payload.traits + analytics.log("Traits in BrazeDestination::identify = ${traits.toString()}") + + braze.getCurrentUser { currentUser -> + if (traits.containsKey(SUBSCRIPTION_GROUP_KEY)) { + val subscriptions = traits[SUBSCRIPTION_GROUP_KEY]?.safeJsonArray + subscriptions?.forEach { subscriptionInfo -> + if (subscriptionInfo != null && subscriptionInfo is JsonObject) { + val groupId = subscriptionInfo.getString(SUBSCRIPTION_ID_KEY) + val groupState = subscriptionInfo.getString(SUBSCRIPTION_STATE_KEY) + if (!groupId.isNullOrBlank()) { + when (groupState) { + "subscribed" -> { + currentUser.addToSubscriptionGroup(groupId) + } + + "unsubscribed" -> { + currentUser.removeFromSubscriptionGroup(groupId) + } + + else -> { + analytics.log( + "Unrecognized Braze subscription state: $groupState." + ) + } + } + } + } + } + } - val currentUser: BrazeUser? = braze.currentUser - if (currentUser == null) { - analytics.log("Braze.getCurrentUser() was null, aborting identify") - return payload - } - - val birthdayString = traits.getString("birthday").orEmpty() - if (birthdayString.isNotBlank()) { - try { - val birthday = Date(birthdayString) - val birthdayCal = Calendar.getInstance(Locale.US) - val month = Month.getMonth(Calendar.MONTH) - birthdayCal.time = birthday - if (month != null) { - currentUser.setDateOfBirth( - birthdayCal[Calendar.YEAR], - month, - birthdayCal[Calendar.DAY_OF_MONTH] - ) - } else { + val birthdayString = traits.getString("birthday").orEmpty() + if (birthdayString.isNotBlank()) { + try { + val birthday = Date(birthdayString) + val birthdayCal = Calendar.getInstance(Locale.US) + val month = Month.getMonth(Calendar.MONTH) + birthdayCal.time = birthday + if (month != null) { + currentUser.setDateOfBirth( + birthdayCal[Calendar.YEAR], + month, + birthdayCal[Calendar.DAY_OF_MONTH] + ) + } else { + analytics.log( + "Could not get birthday month from $birthdayString, skipping." + ) + } + } catch (exception: Exception) { analytics.log( - "Could not get birthday month from $birthdayString, skipping." + "birthday was in an incorrect format, skipping. " + + "The exception is $exception." ) } - } catch (exception: Exception) { - analytics.log( - "birthday was in an incorrect format, skipping. " - + "The exception is $exception." - ) } - } - val email: String = traits.getString("email").orEmpty() - if (email.isNotBlank()) { - currentUser.setEmail(email) - } + val email: String = traits.getString("email").orEmpty() + if (email.isNotBlank()) { + currentUser.setEmail(email) + } - val firstName: String = traits.getString("firstName").orEmpty() - if (firstName.isNotBlank()) { - currentUser.setFirstName(firstName) - } + val firstName: String = traits.getString("firstName").orEmpty() + if (firstName.isNotBlank()) { + currentUser.setFirstName(firstName) + } - val lastName: String = traits.getString("lastName").orEmpty() - if (lastName.isNotBlank()) { - currentUser.setLastName(lastName) - } + val lastName: String = traits.getString("lastName").orEmpty() + if (lastName.isNotBlank()) { + currentUser.setLastName(lastName) + } - val gender: String = traits.getString("gender").orEmpty().uppercase(Locale.getDefault()) - if (gender.isNotBlank()) { - if (MALE_TOKENS.contains( - gender.uppercase( + val gender: String = traits.getString("gender").orEmpty().uppercase(Locale.getDefault()) + if (gender.isNotBlank()) { + if (MALE_TOKENS.contains( + gender.uppercase( Locale.getDefault() ) - ) - ) { - currentUser.setGender(Gender.MALE) - } else if (FEMALE_TOKENS.contains( - gender.uppercase( + ) + ) { + currentUser.setGender(Gender.MALE) + } else if (FEMALE_TOKENS.contains( + gender.uppercase( Locale.getDefault() ) - ) - ) { - currentUser.setGender(Gender.FEMALE) - } - } - - val phone: String = traits.getString("phone").orEmpty() - if (phone.isNotBlank()) { - currentUser.setPhoneNumber(phone) - } - - val address = traits["address"] - if (address != null && address is JsonObject) { - val city: String = address.getString("city").orEmpty() - if (city.isNotBlank()) { - currentUser.setHomeCity(city) - } - val country: String = address.getString("country").orEmpty() - if (country.isNotBlank()) { - currentUser.setCountry(country) + ) + ) { + currentUser.setGender(Gender.FEMALE) + } } - } - for (key: String in traits.keys) { - if (RESERVED_KEYS.contains(key)) { - analytics.log("Skipping reserved key $key") - continue + val phone: String = traits.getString("phone").orEmpty() + if (phone.isNotBlank()) { + currentUser.setPhoneNumber(phone) } - val value = traits[key]?.toContent() - - if (value is Boolean) { - currentUser.setCustomUserAttribute(key, value) - } else if (value is Int) { - currentUser.setCustomUserAttribute(key, value) - } else if (value is Float) { - currentUser.setCustomUserAttribute(key, value) - } else if (value is Double) { - currentUser.setCustomUserAttribute(key, value) - } else if (value is Long) { - currentUser.setCustomUserAttribute(key, value) - } else if (value is String) { - try { - val dateTime = Date(value) - currentUser.setCustomUserAttributeToSecondsFromEpoch(key, dateTime.time) - } catch (_: Exception) { - currentUser.setCustomUserAttribute(key, value) + val address = traits["address"] + if (address != null && address is JsonObject) { + val city: String = address.getString("city").orEmpty() + if (city.isNotBlank()) { + currentUser.setHomeCity(city) } - } else if (value is List<*>) { - val stringValueList = mutableListOf() - for (content in value) { - stringValueList.add(content.toString()) + val country: String = address.getString("country").orEmpty() + if (country.isNotBlank()) { + currentUser.setCountry(country) } - if (stringValueList.size > 0) { - val valueArray: Array = stringValueList.toTypedArray() - currentUser.setCustomAttributeArray( - key, - valueArray - ) + } + + for (key: String in traits.keys) { + if (RESERVED_KEYS.contains(key)) { + analytics.log("Skipping reserved key $key") + continue } - } else if (value is Map<*, *>) { - try { - currentUser.setCustomUserAttribute(key, JSONObject(value)) - } catch (e: Exception) { + + val value = traits[key]?.toContent() + + if (value is Boolean) { + currentUser.setCustomUserAttribute(key, value) + } else if (value is Int) { + currentUser.setCustomUserAttribute(key, value) + } else if (value is Float) { + currentUser.setCustomUserAttribute(key, value) + } else if (value is Double) { + currentUser.setCustomUserAttribute(key, value) + } else if (value is Long) { + currentUser.setCustomUserAttribute(key, value) + } else if (value is String) { + try { + val dateTime = Date(value) + currentUser.setCustomUserAttributeToSecondsFromEpoch(key, dateTime.time) + } catch (_: Exception) { + currentUser.setCustomUserAttribute(key, value) + } + } else if (value is List<*>) { + val stringValueList = mutableListOf() + for (content in value) { + stringValueList.add(content.toString()) + } + if (stringValueList.size > 0) { + val valueArray: Array = stringValueList.toTypedArray() + currentUser.setCustomAttributeArray( + key, + valueArray + ) + } + } else if (value is Map<*, *>) { + try { + currentUser.setCustomUserAttribute(key, JSONObject(value)) + } catch (e: Exception) { + analytics.log( + "Error converting to JSONObject for key $key: ${e.message}" + ) + } + } else { analytics.log( - "Error converting to JSONObject for key $key: ${e.message}" + "Braze can't map segment value for custom Braze user " + + "attribute with key $key and value $value" ) } - } else { - analytics.log( - "Braze can't map segment value for custom Braze user " - + "attribute with key $key and value $value" - ) } } @@ -377,6 +404,10 @@ class BrazeDestination( private const val REVENUE_KEY = "revenue" private const val CURRENCY_KEY = "currency" + private const val SUBSCRIPTION_GROUP_KEY = "braze_subscription_groups" + private const val SUBSCRIPTION_ID_KEY = "subscription_group_id" + private const val SUBSCRIPTION_STATE_KEY = "subscription_state_id" + private val RESERVED_KEYS = listOf( "birthday", "email", @@ -386,7 +417,8 @@ class BrazeDestination( "phone", "address", "anonymousId", - "userId" + "userId", + SUBSCRIPTION_GROUP_KEY ) private fun JsonObject.toBrazeProperties() = diff --git a/braze-segment-kotlin/src/test/java/com/segment/analytics/kotlin/destinations/braze/BrazeSegmentTest.kt b/braze-segment-kotlin/src/test/java/com/segment/analytics/kotlin/destinations/braze/BrazeSegmentTest.kt index d257235..6ef8d20 100644 --- a/braze-segment-kotlin/src/test/java/com/segment/analytics/kotlin/destinations/braze/BrazeSegmentTest.kt +++ b/braze-segment-kotlin/src/test/java/com/segment/analytics/kotlin/destinations/braze/BrazeSegmentTest.kt @@ -3,11 +3,12 @@ package com.segment.analytics.kotlin.destinations.braze import android.app.Activity import android.content.Context import androidx.test.core.app.ApplicationProvider +import com.braze.Braze +import com.braze.BrazeUser import com.braze.enums.Gender import com.braze.enums.Month +import com.braze.events.IValueCallback import com.braze.models.outgoing.AttributionData -import com.braze.Braze -import com.braze.BrazeUser import com.braze.models.outgoing.BrazeProperties import com.braze.support.getOptionalString import com.segment.analytics.kotlin.core.* @@ -130,6 +131,9 @@ class BrazeSegmentTest { } val brazeMock = mock() { on { currentUser } doReturn brazeUserMock + on { getCurrentUser(any()) }.doAnswer { invocation -> + (invocation.arguments[0] as IValueCallback).onSuccess(brazeUserMock) + } } brazeDestination.brazeTestingMock = brazeMock @@ -177,6 +181,9 @@ class BrazeSegmentTest { val brazeUserMock: BrazeUser = mock() val brazeMock = mock() { on { currentUser } doReturn brazeUserMock + on { getCurrentUser(any()) }.doAnswer { invocation -> + (invocation.arguments[0] as IValueCallback).onSuccess(brazeUserMock) + } } brazeDestination.brazeTestingMock = brazeMock @@ -309,11 +316,47 @@ class BrazeSegmentTest { } @Test - fun whenCalledWithExtraProperties_track_callsLogPurchaseWithProperies() { + fun whenCalledWithRevenueButSettingIsFalse_track_DoesNotCallLogPurchaseWithProperties() { + val brazePropertiesCaptor = argumentCaptor() + val brazeMock = mock { + on { logPurchase(any(), any(), any(), brazePropertiesCaptor.capture()) }.then { } + on { logCustomEvent(any(), brazePropertiesCaptor.capture()) }.then { } + } + val settings = getMockSettings(logPurchaseWhenRevenuePresent = false) + brazeDestination.update(settings, Plugin.UpdateType.Initial) + brazeDestination.brazeTestingMock = brazeMock + + val trackEvent = TrackEvent( + JsonObject( + mapOf( + "revenue" to JsonPrimitive(5.99), + "currency" to JsonPrimitive("CAD"), + "color" to JsonPrimitive("red") + + ) + ), + "Random Event Name" + ) + val payload = brazeDestination.track(trackEvent) + assertEquals(payload, trackEvent) + verify(brazeMock, times(0)).logPurchase(any(), any(), any(), any() as BrazeProperties?) + + verify(brazeMock, times(1)).logCustomEvent(eq("Random Event Name"), any() as BrazeProperties?) + + assertEquals(1, brazePropertiesCaptor.allValues.size) + val brazeProperties = brazePropertiesCaptor.firstValue + assertEquals(1, brazeProperties.size) + assertEquals("red", brazeProperties["color"]) + } + + @Test + fun whenCalledWithRevenueButSettingIsFalseButNameIsOrderCompleted_track_DoesCallLogPurchaseWithProperties() { val brazePropertiesCaptor = argumentCaptor() val brazeMock = mock { on { logPurchase(any(), any(), any(), brazePropertiesCaptor.capture()) }.then { } } + val settings = getMockSettings(logPurchaseWhenRevenuePresent = false) + brazeDestination.update(settings, Plugin.UpdateType.Initial) brazeDestination.brazeTestingMock = brazeMock val trackEvent = TrackEvent( @@ -337,6 +380,37 @@ class BrazeSegmentTest { assertEquals("red", brazeProperties["color"]) } + @Test + fun whenCalledWithRevenueAndSettingIsTrueButNameIsNotOrderCompleted_track_DoesCallLogPurchaseWithProperties() { + val brazePropertiesCaptor = argumentCaptor() + val brazeMock = mock { + on { logPurchase(any(), any(), any(), brazePropertiesCaptor.capture()) }.then { } + } + val settings = getMockSettings(logPurchaseWhenRevenuePresent = true) + brazeDestination.update(settings, Plugin.UpdateType.Initial) + brazeDestination.brazeTestingMock = brazeMock + + val trackEvent = TrackEvent( + JsonObject( + mapOf( + "revenue" to JsonPrimitive(5.99), + "currency" to JsonPrimitive("CAD"), + "color" to JsonPrimitive("red") + + ) + ), + "Random Event Name" + ) + val payload = brazeDestination.track(trackEvent) + assertEquals(payload, trackEvent) + verify(brazeMock, times(1)).logPurchase(eq("Random Event Name"), any(), any(), any() as BrazeProperties?) + + assertEquals(1, brazePropertiesCaptor.allValues.size) + val brazeProperties = brazePropertiesCaptor.firstValue + assertEquals(1, brazeProperties.size) + assertEquals("red", brazeProperties["color"]) + } + @Test fun whenEventNameIsInstallAttributed_track_callsSetAttributionData() { val attributionDataCaptor = argumentCaptor() @@ -346,6 +420,9 @@ class BrazeSegmentTest { } val brazeMock = mock() { on { currentUser } doReturn brazeUserMock + on { getCurrentUser(any()) }.doAnswer { invocation -> + (invocation.arguments[0] as IValueCallback).onSuccess(brazeUserMock) + } } brazeDestination.brazeTestingMock = brazeMock @@ -376,16 +453,82 @@ class BrazeSegmentTest { verifyNoMoreInteractions(brazeUserMock) } + @Test + fun whenCalledWithSubscriptionData_track_callsAddToSubscriptionGroup() { + val groupIdCaptor = argumentCaptor() + val brazeUserMock: BrazeUser = mock() { + on { addToSubscriptionGroup(groupIdCaptor.capture()) }.thenReturn(true) + } + + val brazeMock = mock() { + on { currentUser } doReturn brazeUserMock + on { getCurrentUser(any()) }.doAnswer { invocation -> + (invocation.arguments[0] as IValueCallback).onSuccess(brazeUserMock) + } + } + brazeDestination.brazeTestingMock = brazeMock + + val identifyEvent = IdentifyEvent( + "myUser", + getMockSubscriptionTraits(true) + ) + val payload = brazeDestination.identify(identifyEvent) + assertEquals(payload, identifyEvent) + verify(brazeUserMock, times(1)).addToSubscriptionGroup(any()) + assertEquals("123-456-789", groupIdCaptor.firstValue) + + // Verify the other data is handled + verify(brazeUserMock, times(1)).setFirstName(FIRST_NAME) + verify(brazeUserMock, times(1)).setLastName(LAST_NAME) + + // Verify that nothing else was called (subscription data didn't turn into the custom attributes) + verifyNoMoreInteractions(brazeUserMock) + } + + @Test + fun whenCalledWithUnSubscriptionData_identify_callsRemoveFromSubscriptionGroup() { + val groupIdCaptor = argumentCaptor() + val brazeUserMock: BrazeUser = mock() { + on { removeFromSubscriptionGroup(groupIdCaptor.capture()) }.thenReturn(true) + } + + val brazeMock = mock() { + on { currentUser } doReturn brazeUserMock + on { getCurrentUser(any()) }.doAnswer { invocation -> + (invocation.arguments[0] as IValueCallback).onSuccess(brazeUserMock) + } + } + brazeDestination.brazeTestingMock = brazeMock + + val identifyEvent = IdentifyEvent( + "myUser", + getMockSubscriptionTraits(false) + ) + val payload = brazeDestination.identify(identifyEvent) + assertEquals(payload, identifyEvent) + verify(brazeUserMock, times(1)).removeFromSubscriptionGroup(any()) + assertEquals("123-456-789", groupIdCaptor.firstValue) + + // Verify the other data is handled + verify(brazeUserMock, times(1)).setFirstName(FIRST_NAME) + verify(brazeUserMock, times(1)).setLastName(LAST_NAME) + + // Verify that nothing else was called (subscription data didn't turn into the custom attributes) + verifyNoMoreInteractions(brazeUserMock) + } + private fun getMockSettings( apiKey: String = API_KEY, customEndpoint: String = CUSTOM_ENDPOINT, - autoInAppRegistration: Boolean = true + autoInAppRegistration: Boolean = true, + logPurchaseWhenRevenuePresent: Boolean = true ): Settings { val brazeJsonSettings = JsonObject( content = mapOf( "apiKey" to JsonPrimitive(apiKey), "customEndpoint" to JsonPrimitive(customEndpoint), - "automatic_in_app_message_registration_enabled" to JsonPrimitive(autoInAppRegistration) + "automatic_in_app_message_registration_enabled" to JsonPrimitive(autoInAppRegistration), + "logPurchaseWhenRevenuePresent" to JsonPrimitive(logPurchaseWhenRevenuePresent) ) ) val integrations = JsonObject(mapOf("Appboy" to brazeJsonSettings)) @@ -439,6 +582,36 @@ class BrazeSegmentTest { ) } + private fun getMockSubscriptionTraits(subscription: Boolean = true): Traits { + val groupId = "123-456-789" + val groupStatus = if (subscription) { + "subscribed" + } else { + "unsubscribed" + } + + val subscriptionData = JsonArray( + listOf( + JsonObject( + mapOf( + "subscription_group_id" to JsonPrimitive(groupId), + "subscription_state_id" to JsonPrimitive(groupStatus) + ) + ) + ) + ) + + val contentMap = mutableMapOf( + "firstName" to JsonPrimitive(FIRST_NAME), + "lastName" to JsonPrimitive(LAST_NAME), + "braze_subscription_groups" to subscriptionData + ) + + return JsonObject( + content = contentMap + ) + } + @OptIn(ExperimentalContracts::class) fun ktAssertNotNull(thing: Any?, failureMessage: String? = null) { contract { diff --git a/build.gradle b/build.gradle index f4f3496..15358d6 100644 --- a/build.gradle +++ b/build.gradle @@ -15,4 +15,26 @@ plugins { id 'com.android.application' version "$ANDROID_APPLICATION_PLUGIN" apply false id 'com.android.library' version "$ANDROID_LIBRARY_PLUGIN" apply false id 'org.jetbrains.kotlin.android' version "$KOTLIN_ANDROID_PLUGIN" apply false + id 'signing' + id "io.github.gradle-nexus.publish-plugin" version "${GRADLE_NEXUS_PUBLISH_PLUGIN_VERSION}" +} + +apply plugin: 'io.github.gradle-nexus.publish-plugin' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + packageGroup = "com.braze" + if (isReleaseBuild.toBoolean()) { + def secureProps = new Properties() + rootProject.file(findProperty("MAVEN_CENTRAL_SONATYPE_SECRETS_FILEPATH")).withInputStream { secureProps.load(it) } + username = secureProps["sonatype.username"] + password = secureProps["sonatype.password"] + } + } + } } diff --git a/gradle.properties b/gradle.properties index 2cc8afe..e9f1e64 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,10 +24,10 @@ android.nonTransitiveRClass=true isReleaseBuild=false -LIBRARY_VERSION=1.3.0 +LIBRARY_VERSION=1.4.0 -BRAZE_SDK_VERSION=25.0.0 -SEGMENT_SDK_VERSION=1.10.5 +BRAZE_SDK_VERSION=26.1.0 +SEGMENT_SDK_VERSION=1.13.0 ANDROIDX_CORE_KTX_VERSION=1.9.0 APPCOMPAT_VERSION=1.5.1 diff --git a/sample-app/src/main/java/com/segment/analytics/kotlin/brazesample/testapp/MainActivity.kt b/sample-app/src/main/java/com/segment/analytics/kotlin/brazesample/testapp/MainActivity.kt index 75b04fb..d3c36d4 100644 --- a/sample-app/src/main/java/com/segment/analytics/kotlin/brazesample/testapp/MainActivity.kt +++ b/sample-app/src/main/java/com/segment/analytics/kotlin/brazesample/testapp/MainActivity.kt @@ -4,9 +4,12 @@ import android.os.Bundle import android.util.Log import android.widget.Button import androidx.appcompat.app.AppCompatActivity +import com.braze.enums.BrazeDateFormat import com.braze.support.BrazeLogger +import com.braze.support.formatDate import com.segment.analytics.kotlin.brazesample.testapp.databinding.ActivityMainBinding import kotlinx.serialization.json.* +import java.util.* class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding @@ -21,7 +24,8 @@ class MainActivity : AppCompatActivity() { } findViewById