Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
megamegax committed Sep 19, 2023
2 parents 5693c4c + 915f946 commit af0b769
Show file tree
Hide file tree
Showing 31 changed files with 575 additions and 351 deletions.
12 changes: 5 additions & 7 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

### [Emarsys SDK](https://github.com/emartech/android-emarsys-sdk)

* Kotlin updated to 1.9.0
* jvmToolchain updated to Java 17
* targetApi level updated to 34
* Proguard rule added for Emarsys SDK to prevent issues when host application uses R8# What's
changed

# What's fixed

### [In-App](https://github.com/emartech/android-emarsys-sdk/wiki#3-inapp)
### [Emarsys SDK](https://github.com/emartech/android-emarsys-sdk)

* The SDK could crash in some rare cases during WebView creation
* Prevent requests without an application code from being sent and return an
InvalidArgumentException instead through the CompletionListener
18 changes: 18 additions & 0 deletions emarsys-sdk/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/jpollak/Library/Android/sdk/tools/proguard/proguard-android.txt
# 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 *;
#}
-keep public class com.emarsys.** { *; }
12 changes: 6 additions & 6 deletions emarsys-sdk/src/main/java/com/emarsys/Emarsys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ object Emarsys {
completionListener: CompletionListener? = null
) {
if (FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
|| !FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT)
|| (!FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT))
) {
EmarsysDependencyInjection.mobileEngageApi()
.proxyApi(mobileEngage().concurrentHandlerHolder)
Expand All @@ -129,8 +129,8 @@ object Emarsys {
completionListener: CompletionListener? = null
) {
if (FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
|| !FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT)
|| (!FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT))
) {
EmarsysDependencyInjection.mobileEngageApi()
.proxyApi(mobileEngage().concurrentHandlerHolder)
Expand All @@ -147,8 +147,8 @@ object Emarsys {
@JvmOverloads
fun clearContact(completionListener: CompletionListener? = null) {
if (FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
|| !FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT)
|| (!FeatureRegistry.isFeatureEnabled(MOBILE_ENGAGE)
&& !FeatureRegistry.isFeatureEnabled(PREDICT))
) {
EmarsysDependencyInjection.mobileEngageApi()
.proxyApi(mobileEngage().concurrentHandlerHolder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,8 @@ open class DefaultEmarsysComponent(config: EmarsysConfig) : EmarsysComponent {
uuidProvider,
eventServiceInternal,
sessionIdHolder,
contactTokenStorage
contactTokenStorage,
requestContext
)
}

Expand Down
2 changes: 1 addition & 1 deletion emarsys-sdk/src/main/java/com/emarsys/push/Push.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Push(private val loggingInstance: Boolean = false) : PushApi {
}
set(value) {
(if (loggingInstance) mobileEngage().loggingPushInternal else mobileEngage().pushInternal)
.setPushToken(value, null)
.setPushToken(value!!, null)
}

override fun setPushToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class DefaultConfigInternalTest {
latch.await()

verify(mockPushInternal).pushToken
verify(mockMobileEngageRequestContext).applicationCode
verify(mockMobileEngageRequestContext, times(2)).applicationCode
verify(mockMobileEngageRequestContext).hasContactIdentification()
verify(mockPushInternal).clearPushToken(any())
verify(mockMobileEngageInternal).clearContact(any())
Expand Down Expand Up @@ -330,7 +330,7 @@ class DefaultConfigInternalTest {

latch.await()

verify(mockMobileEngageRequestContext).applicationCode
verify(mockMobileEngageRequestContext, times(2)).applicationCode
verify(mockMobileEngageRequestContext).applicationCode = OTHER_APPLICATION_CODE
verify(mockPushInternal).setPushToken(eq(PUSH_TOKEN), any())

Expand Down Expand Up @@ -448,6 +448,24 @@ class DefaultConfigInternalTest {
verify(mockMobileEngageRequestContext).applicationCode = null
}

@Test
fun testChangeApplicationCode_whenApplicationCodeIsMissing_shouldNotCleanPushTokenButWorkProperly() {
whenever(mockMobileEngageRequestContext.applicationCode).thenReturn(null)
whenever(mockPushInternal.pushToken).thenReturn(PUSH_TOKEN)

val latch = CountDownLatch(1)
val completionListener = CompletionListener {
latch.countDown()
}
configInternal.changeApplicationCode(OTHER_APPLICATION_CODE, completionListener)
latch.await()

verify(mockPushInternal, times(0)).clearPushToken(any())
FeatureRegistry.isFeatureEnabled(InnerFeature.MOBILE_ENGAGE) shouldBe true
FeatureRegistry.isFeatureEnabled(InnerFeature.EVENT_SERVICE_V4) shouldBe true
verify(mockMobileEngageRequestContext).applicationCode = OTHER_APPLICATION_CODE
}

@Test
fun testChangeApplicationCode_whenClearPushToken_butPushTokenHasNotBeenSetPreviously_callOnSuccess() {
whenever(mockPushInternal.clearPushToken(any())).thenAnswer { invocation ->
Expand Down Expand Up @@ -481,7 +499,7 @@ class DefaultConfigInternalTest {
latch.await()

verify(mockPushInternal).pushToken
verify(mockMobileEngageRequestContext).applicationCode
verify(mockMobileEngageRequestContext, times(2)).applicationCode
verify(mockMobileEngageRequestContext).hasContactIdentification()
verify(mockMobileEngageRequestContext).applicationCode = null
verify(mockPushInternal).clearPushToken(any())
Expand Down
67 changes: 42 additions & 25 deletions emarsys/src/main/java/com/emarsys/config/DefaultConfigInternal.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,25 @@ import com.emarsys.predict.request.PredictRequestContext
import java.util.concurrent.CountDownLatch

@Mockable
class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngageRequestContext,
private val mobileEngageInternal: MobileEngageInternal,
private val pushInternal: PushInternal,
private val predictRequestContext: PredictRequestContext,
private val deviceInfo: DeviceInfo,
private val requestManager: RequestManager,
private val emarsysRequestModelFactory: EmarsysRequestModelFactory,
private val configResponseMapper: RemoteConfigResponseMapper,
private val clientServiceStorage: Storage<String?>,
private val eventServiceStorage: Storage<String?>,
private val deeplinkServiceStorage: Storage<String?>,
private val predictServiceStorage: Storage<String?>,
private val messageInboxServiceStorage: Storage<String?>,
private val logLevelStorage: Storage<String?>,
private val crypto: Crypto,
private val clientServiceInternal: ClientServiceInternal,
private val concurrentHandlerHolder: ConcurrentHandlerHolder) : ConfigInternal {
class DefaultConfigInternal(
private val mobileEngageRequestContext: MobileEngageRequestContext,
private val mobileEngageInternal: MobileEngageInternal,
private val pushInternal: PushInternal,
private val predictRequestContext: PredictRequestContext,
private val deviceInfo: DeviceInfo,
private val requestManager: RequestManager,
private val emarsysRequestModelFactory: EmarsysRequestModelFactory,
private val configResponseMapper: RemoteConfigResponseMapper,
private val clientServiceStorage: Storage<String?>,
private val eventServiceStorage: Storage<String?>,
private val deeplinkServiceStorage: Storage<String?>,
private val predictServiceStorage: Storage<String?>,
private val messageInboxServiceStorage: Storage<String?>,
private val logLevelStorage: Storage<String?>,
private val crypto: Crypto,
private val clientServiceInternal: ClientServiceInternal,
private val concurrentHandlerHolder: ConcurrentHandlerHolder
) : ConfigInternal {

override val applicationCode: String?
get() = mobileEngageRequestContext.applicationCode
Expand All @@ -68,12 +70,16 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage
get() = deviceInfo.sdkVersion


override fun changeApplicationCode(applicationCode: String?, completionListener: CompletionListener?) {
override fun changeApplicationCode(
applicationCode: String?,
completionListener: CompletionListener?
) {
val pushToken: String? = pushInternal.pushToken
val hasContactIdentification = mobileEngageRequestContext.hasContactIdentification()
var throwable: Throwable? = null
concurrentHandlerHolder.postOnBackground {
if (pushToken != null) {

if (pushToken != null && mobileEngageRequestContext.applicationCode != null) {
throwable = clearPushToken()
}
if ((throwable == null) && (mobileEngageRequestContext.applicationCode != null) && hasContactIdentification) {
Expand All @@ -94,13 +100,14 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage
if (throwable != null) {
handleAppCodeChange(null)
}

concurrentHandlerHolder.postOnMain {
completionListener?.onCompleted(throwable)
}
}
}

private fun synchronizeMethodWithRunnerCallback(runnerCallback: (CompletionListener)->(Unit)): Throwable? {
private fun synchronizeMethodWithRunnerCallback(runnerCallback: (CompletionListener) -> (Unit)): Throwable? {
var result: Throwable? = null
val latch = CountDownLatch(1)
val completionListener = CompletionListener {
Expand Down Expand Up @@ -162,7 +169,11 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage
signatureResponse.result?.let { signature ->
fetchRemoteConfig(ResultListener {
it.result?.let { remoteConfigResponseModel ->
if (crypto.verify(remoteConfigResponseModel.body!!.toByteArray(), signature)) {
if (crypto.verify(
remoteConfigResponseModel.body!!.toByteArray(),
signature
)
) {
applyRemoteConfig(configResponseMapper.map(remoteConfigResponseModel))
completionListener?.onCompleted(null)
} else {
Expand Down Expand Up @@ -195,10 +206,13 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage
}

override fun onError(id: String, responseModel: ResponseModel) {
val response = Try.failure<String>(ResponseErrorException(
val response = Try.failure<String>(
ResponseErrorException(
responseModel.statusCode,
responseModel.message,
responseModel.body))
responseModel.body
)
)

resultListener.onResult(response)
}
Expand All @@ -221,10 +235,13 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage
}

override fun onError(id: String, responseModel: ResponseModel) {
val response = Try.failure<ResponseModel>(ResponseErrorException(
val response = Try.failure<ResponseModel>(
ResponseErrorException(
responseModel.statusCode,
responseModel.message,
responseModel.body))
responseModel.body
)
)

resultListener.onResult(response)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.mockito.kotlin.*
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.concurrent.CountDownLatch

class MobileEngageRefreshTokenInternalTest {
Expand Down Expand Up @@ -51,21 +57,6 @@ class MobileEngageRefreshTokenInternalTest {
)
}

@Test(expected = IllegalArgumentException::class)
fun testConstructor_tokenResponseHandler_mustNotBeNull() {
MobileEngageRefreshTokenInternal(null, mockRestClient, mockRequestModelFactory)
}

@Test(expected = IllegalArgumentException::class)
fun testConstructor_requestManager_mustNotBeNull() {
MobileEngageRefreshTokenInternal(mockResponseHandler, null, mockRequestModelFactory)
}

@Test(expected = IllegalArgumentException::class)
fun testConstructor_requestModelFactory_mustNotBeNull() {
MobileEngageRefreshTokenInternal(mockResponseHandler, mockRestClient, null)
}

@Test
fun testRefreshContactToken_shouldCallSubmitNow() {
refreshTokenInternal.refreshContactToken(mockCompletionListener)
Expand Down Expand Up @@ -144,4 +135,16 @@ class MobileEngageRefreshTokenInternalTest {
latch.await()
verify(mockCompletionListener).onCompleted(any<Exception>())
}

@Test
fun testRefreshContactToken_shouldCallCompletionListener_whenRequestModelFactoryThrowsIllegalArgumentException() {
val mockCompletionListener: CompletionListener = mock()
whenever(mockRequestModelFactory.createRefreshContactTokenRequest()).thenThrow(
IllegalArgumentException("")
)

refreshTokenInternal.refreshContactToken(mockCompletionListener)

verify(mockCompletionListener).onCompleted(any<Exception>())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import com.emarsys.mobileengage.RefreshTokenInternal

class FakeMobileEngageRefreshTokenInternal(private val success: Boolean = false) : RefreshTokenInternal {

override fun refreshContactToken(completionListener: CompletionListener) {
override fun refreshContactToken(completionListener: CompletionListener?) {
if (success) {
completionListener.onCompleted(null)
completionListener?.onCompleted(null)
} else {
completionListener.onCompleted(Exception())
completionListener?.onCompleted(Exception())
}
}
}
Loading

0 comments on commit af0b769

Please sign in to comment.