diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt index 067c3f56c..806e751ee 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/CoreConstants.kt @@ -13,7 +13,7 @@ package com.adobe.marketing.mobile.internal internal object CoreConstants { const val LOG_TAG = "MobileCore" - const val VERSION = "2.6.1" + const val VERSION = "2.6.2" object EventDataKeys { /** diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/internal/eventhub/history/AndroidEventHistory.java b/code/core/src/main/java/com/adobe/marketing/mobile/internal/eventhub/history/AndroidEventHistory.java index 6cdf2d859..e3c9b8025 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/internal/eventhub/history/AndroidEventHistory.java +++ b/code/core/src/main/java/com/adobe/marketing/mobile/internal/eventhub/history/AndroidEventHistory.java @@ -84,7 +84,8 @@ public void run() { * matching events in the {@code EventHistoryDatabase} if an "any" search was done. If an * "ordered" search was done, the handler will contain a "1" if the event history requests * were found in the order specified in the eventHistoryRequests array and a "0" if the - * events were not found in the order specified. + * events were not found in the order specified. In case of a database failure, the handler + * will receive a "-1" regardless of the enforceOrder flag. */ @Override public void getEvents( @@ -113,6 +114,12 @@ public void run() { final Cursor result = androidEventHistoryDatabase.select(eventHash, from, to); + // if the database operation failed, return -1 + if (result == null) { + notifyHandler(handler, -1); + return; + } + int currentResult = 0; try { // columns are index 0: count, index 1: oldest, index 2: diff --git a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt index da0fc6bfd..fb25f7d13 100644 --- a/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt +++ b/code/core/src/main/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequence.kt @@ -151,24 +151,29 @@ internal class LaunchRulesConsequence( return RuleConsequence(consequence.id, consequence.type, tokenReplacedMap) } + private fun replaceToken(value: Any?, tokenFinder: TokenFinder): Any? { + return when (value) { + is String -> replaceToken(value, tokenFinder) + is Map<*, *> -> replaceToken(EventDataUtils.castFromGenericType(value), tokenFinder) + is List<*> -> replaceToken(value, tokenFinder) + else -> value + } + } + private fun replaceToken(detail: Map?, tokenFinder: TokenFinder): Map? { if (detail.isNullOrEmpty()) { return null } val mutableDetail = detail.toMutableMap() for ((key, value) in detail) { - when (value) { - is String -> mutableDetail[key] = replaceToken(value, tokenFinder) - is Map<*, *> -> mutableDetail[key] = replaceToken( - EventDataUtils.castFromGenericType(value), - tokenFinder - ) - else -> continue - } + mutableDetail[key] = replaceToken(value, tokenFinder) } return mutableDetail } + private fun replaceToken(value: List, tokenFinder: TokenFinder): List { + return value.map { replaceToken(it, tokenFinder) } + } private fun replaceToken(value: String, tokenFinder: TokenFinder): String { val template = Template(value, DelimiterPair(LAUNCH_RULE_TOKEN_LEFT_DELIMITER, LAUNCH_RULE_TOKEN_RIGHT_DELIMITER)) return template.render(tokenFinder, LaunchRuleTransformer.createTransforming()) diff --git a/code/core/src/phone/java/com/adobe/marketing/mobile/MobileCore.java b/code/core/src/phone/java/com/adobe/marketing/mobile/MobileCore.java index 43e7af221..2ddcd27a7 100644 --- a/code/core/src/phone/java/com/adobe/marketing/mobile/MobileCore.java +++ b/code/core/src/phone/java/com/adobe/marketing/mobile/MobileCore.java @@ -105,15 +105,19 @@ public static void setApplication(@NonNull final Application application) { return; } - // AMSDK-8502 - // Workaround to prevent a crash happening on Android 8.0/8.1 related to TimeZoneNamesImpl - // https://issuetracker.google.com/issues/110848122 - try { - new Date().toString(); - } catch (AssertionError e) { - // Workaround for a bug in Android that can cause crashes on Android 8.0 and 8.1 - } catch (Exception e) { - // Workaround for a bug in Android that can cause crashes on Android 8.0 and 8.1 + if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.O + || android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.O_MR1) { + // AMSDK-8502 + // Workaround to prevent a crash happening on Android 8.0/8.1 related to + // TimeZoneNamesImpl + // https://issuetracker.google.com/issues/110848122 + try { + new Date().toString(); + } catch (AssertionError e) { + // Workaround for a bug in Android that can cause crashes on Android 8.0 and 8.1 + } catch (Exception e) { + // Workaround for a bug in Android that can cause crashes on Android 8.0 and 8.1 + } } ServiceProvider.getInstance().getAppContextService().setApplication(application); diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt index 33a705275..eac5a7cd7 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/MobileCoreTests.kt @@ -35,7 +35,7 @@ import kotlin.test.assertTrue @RunWith(MockitoJUnitRunner.Silent::class) class MobileCoreTests { - private var EXTENSION_VERSION = "2.6.1" + private var EXTENSION_VERSION = "2.6.2" @Mock private lateinit var mockedEventHub: EventHub diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt index 3bf66bac7..22d03b3c4 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/internal/configuration/ConfigurationExtensionTests.kt @@ -60,7 +60,7 @@ import kotlin.test.assertTrue @RunWith(MockitoJUnitRunner.Silent::class) class ConfigurationExtensionTests { - private var EXTENSION_VERSION = "2.6.1" + private var EXTENSION_VERSION = "2.6.2" @Mock private lateinit var mockServiceProvider: ServiceProvider diff --git a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequenceTests.kt b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequenceTests.kt index 8c7ac9a60..05eaaf62b 100644 --- a/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequenceTests.kt +++ b/code/core/src/test/java/com/adobe/marketing/mobile/launch/rulesengine/LaunchRulesConsequenceTests.kt @@ -37,6 +37,7 @@ import org.mockito.kotlin.reset import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.test.assertTrue @RunWith(MockitoJUnitRunner.Silent::class) class LaunchRulesConsequenceTests { @@ -118,6 +119,79 @@ class LaunchRulesConsequenceTests { assertEquals("", attachedData["launches"]) } + @Test + fun `Test Attach Data Array`() { + // / Given: a launch rule to attach data to event + + // ---------- attach data rule ---------- + // "eventdata": { + // "attached_data_array": [ + // "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.carriername%}", + // "testStringTopLevel", + // { + // "testDictKey": "testVal", + // "osversionNested": "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.osversion%}" + // "numbers": 123 + // + // }, [ + // "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.osversion%}", + // "testStringInsideNestedArray" + // ] + // ] + // } + // -------------------------------------- + resetRulesEngine("rules_module_tests/consequence_rules_testAttachData_array.json") + + // / When: evaluating a launch event + + // ------------ launch event ------------ + // "eventdata": { + // "lifecyclecontextdata": { + // "launchevent": "LaunchEvent" + // } + // } + // -------------------------------------- + `when`(extensionApi.getSharedState(anyString(), any(), anyBoolean(), any())).thenReturn( + SharedStateResult( + SharedStateStatus.SET, + mapOf( + "lifecyclecontextdata" to mapOf( + "carriername" to "AT&T", + "osversion" to "27" + ) + ) + ) + ) + + val matchedRules = rulesEngine.evaluate(LaunchTokenFinder(defaultEvent, extensionApi)) + val processedEvent = + launchRulesConsequence.process(defaultEvent, matchedRules) + + // / Then: no consequence event will be dispatched + verify(extensionApi, never()).dispatch(any()) + + val attachedDataArray = processedEvent.eventData?.get("attached_data_array") as List<*> + + // / Then: "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.carriername%}" should be replaced with "AT&T" + assertEquals("AT&T", attachedDataArray[0]) + + // / Then: "testStringTopLevel" should not be changed + assertEquals("testStringTopLevel", attachedDataArray[1]) + + // / Then: the nested map should be handled correctly + val nestedMap = attachedDataArray[2] as Map<*, *> + + assertEquals("testVal", nestedMap["testDictKey"] as String) + assertEquals("27", nestedMap["osversionNested"] as String) + assertTrue { nestedMap["number"] is Int } + assertEquals(123, nestedMap["number"] as Int) + + // / Then: the data array should be handled correctly + val dataArray = attachedDataArray[3] as List<*> + assertEquals("27", dataArray[0] as String) + assertEquals("testStringInsideNestedArray", dataArray[1] as String) + } + @Test fun `Test Attach Data Invalid Json`() { // / Given: a launch rule to attach data to event diff --git a/code/core/src/test/resources/rules_module_tests/consequence_rules_testAttachData_array.json b/code/core/src/test/resources/rules_module_tests/consequence_rules_testAttachData_array.json new file mode 100644 index 000000000..a58b8c30f --- /dev/null +++ b/code/core/src/test/resources/rules_module_tests/consequence_rules_testAttachData_array.json @@ -0,0 +1,101 @@ +{ + "version": 1, + "rules": [ + { + "condition": { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "group", + "definition": { + "logic": "or", + "conditions": [ + { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~type", + "matcher": "eq", + "values": [ + "com.adobe.eventType.lifecycle" + ] + } + }, + { + "type": "matcher", + "definition": { + "key": "~source", + "matcher": "eq", + "values": [ + "com.adobe.eventSource.responseContent" + ] + } + }, + { + "type": "matcher", + "definition": { + "key": "lifecyclecontextdata.launchevent", + "matcher": "ex", + "values": [] + } + } + ] + } + } + ] + } + }, + { + "type": "group", + "definition": { + "logic": "and", + "conditions": [ + { + "type": "matcher", + "definition": { + "key": "~state.com.adobe.module.lifecycle/lifecyclecontextdata.carriername", + "matcher": "eq", + "values": [ + "AT&T" + ] + } + } + ] + } + } + ] + } + }, + "consequences": [ + { + "id": "RCa839e401f54a459a9049328f9b609a07", + "type": "add", + "detail": { + "eventdata": { + "attached_data_array": [ + "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.carriername%}", + "testStringTopLevel", + { + "testDictKey": "testVal", + "osversionNested": "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.osversion%}", + "number": 123 + }, [ + "{%~state.com.adobe.module.lifecycle/lifecyclecontextdata.osversion%}", + "testStringInsideNestedArray", + null + ], + null + ] + } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/code/gradle.properties b/code/gradle.properties index f52513875..af216dba9 100644 --- a/code/gradle.properties +++ b/code/gradle.properties @@ -6,7 +6,7 @@ android.useAndroidX=true # #Maven artifacts #Core extension -coreExtensionVersion=2.6.1 +coreExtensionVersion=2.6.2 coreExtensionName=core coreExtensionAARName=core-phone-release.aar coreMavenRepoName=AdobeMobileCoreSdk