From 95a41270997787350a1b0deeea962d078a8c7953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyady=20Mih=C3=A1ly?= Date: Mon, 18 Sep 2023 15:26:53 +0200 Subject: [PATCH 1/3] feat(emarsys): Make requests null applicationCode proof SUITEDEV-34202 Co-authored-by: davidSchuppa <32750715+davidSchuppa@users.noreply.github.com> Co-authored-by: LasOri <24588073+LasOri@users.noreply.github.com> --- emarsys-sdk/proguard-rules.pro | 18 ++ .../src/main/java/com/emarsys/Emarsys.kt | 12 +- .../com/emarsys/di/DefaultEmarsysComponent.kt | 3 +- .../src/main/java/com/emarsys/push/Push.kt | 2 +- .../config/DefaultConfigInternalTest.kt | 24 ++- .../emarsys/config/DefaultConfigInternal.kt | 67 ++++--- .../MobileEngageRefreshTokenInternalTest.kt | 35 ++-- .../FakeMobileEngageRefreshTokenInternal.kt | 6 +- .../session/MobileEngageSessionTest.kt | 64 ++++++- .../DefaultMobileEngageInternal.kt | 40 ++-- .../MobileEngageRefreshTokenInternal.java | 51 ----- .../MobileEngageRefreshTokenInternal.kt | 45 +++++ .../mobileengage/RefreshTokenInternal.java | 8 - .../mobileengage/RefreshTokenInternal.kt | 7 + .../client/ClientServiceInternal.java | 8 - .../client/ClientServiceInternal.kt | 7 + .../client/DefaultClientServiceInternal.java | 23 --- .../client/DefaultClientServiceInternal.kt | 19 ++ .../client/LoggingClientServiceInternal.kt | 2 +- .../mobileengage/endpoint/Endpoint.java | 47 ----- .../emarsys/mobileengage/endpoint/Endpoint.kt | 42 +++++ .../event/DefaultEventServiceInternal.kt | 30 ++- .../geofence/DefaultGeofenceInternal.kt | 42 +++-- .../inbox/DefaultMessageInboxInternal.kt | 8 +- .../mobileengage/push/DefaultPushInternal.kt | 5 +- .../mobileengage/push/LoggingPushInternal.kt | 37 ++-- .../mobileengage/push/PushInternal.java | 25 --- .../emarsys/mobileengage/push/PushInternal.kt | 17 ++ .../MobileEngageRequestModelFactory.kt | 177 ++++++++++++++---- .../session/MobileEngageSession.kt | 43 +++-- .../com/emarsys/sample/SampleApplication.kt | 2 +- 31 files changed, 571 insertions(+), 345 deletions(-) create mode 100644 emarsys-sdk/proguard-rules.pro delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.kt delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.kt delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.kt delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.kt delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.kt delete mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.java create mode 100644 mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.kt diff --git a/emarsys-sdk/proguard-rules.pro b/emarsys-sdk/proguard-rules.pro new file mode 100644 index 000000000..856d56b7d --- /dev/null +++ b/emarsys-sdk/proguard-rules.pro @@ -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.** { *; } \ No newline at end of file diff --git a/emarsys-sdk/src/main/java/com/emarsys/Emarsys.kt b/emarsys-sdk/src/main/java/com/emarsys/Emarsys.kt index 4b3be1938..bb6ea8bf3 100644 --- a/emarsys-sdk/src/main/java/com/emarsys/Emarsys.kt +++ b/emarsys-sdk/src/main/java/com/emarsys/Emarsys.kt @@ -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) @@ -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) @@ -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) diff --git a/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt b/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt index df317b1b1..b3f84c185 100644 --- a/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt +++ b/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt @@ -665,7 +665,8 @@ open class DefaultEmarsysComponent(config: EmarsysConfig) : EmarsysComponent { uuidProvider, eventServiceInternal, sessionIdHolder, - contactTokenStorage + contactTokenStorage, + requestContext ) } diff --git a/emarsys-sdk/src/main/java/com/emarsys/push/Push.kt b/emarsys-sdk/src/main/java/com/emarsys/push/Push.kt index 37f14cb0a..05ecd31fb 100644 --- a/emarsys-sdk/src/main/java/com/emarsys/push/Push.kt +++ b/emarsys-sdk/src/main/java/com/emarsys/push/Push.kt @@ -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( diff --git a/emarsys/src/androidTest/java/com/emarsys/config/DefaultConfigInternalTest.kt b/emarsys/src/androidTest/java/com/emarsys/config/DefaultConfigInternalTest.kt index 35bf00b2e..c2d97ed11 100644 --- a/emarsys/src/androidTest/java/com/emarsys/config/DefaultConfigInternalTest.kt +++ b/emarsys/src/androidTest/java/com/emarsys/config/DefaultConfigInternalTest.kt @@ -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()) @@ -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()) @@ -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 -> @@ -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()) diff --git a/emarsys/src/main/java/com/emarsys/config/DefaultConfigInternal.kt b/emarsys/src/main/java/com/emarsys/config/DefaultConfigInternal.kt index 7845a3207..6138fd2ae 100644 --- a/emarsys/src/main/java/com/emarsys/config/DefaultConfigInternal.kt +++ b/emarsys/src/main/java/com/emarsys/config/DefaultConfigInternal.kt @@ -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, - private val eventServiceStorage: Storage, - private val deeplinkServiceStorage: Storage, - private val predictServiceStorage: Storage, - private val messageInboxServiceStorage: Storage, - private val logLevelStorage: Storage, - 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, + private val eventServiceStorage: Storage, + private val deeplinkServiceStorage: Storage, + private val predictServiceStorage: Storage, + private val messageInboxServiceStorage: Storage, + private val logLevelStorage: Storage, + private val crypto: Crypto, + private val clientServiceInternal: ClientServiceInternal, + private val concurrentHandlerHolder: ConcurrentHandlerHolder +) : ConfigInternal { override val applicationCode: String? get() = mobileEngageRequestContext.applicationCode @@ -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) { @@ -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 { @@ -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 { @@ -195,10 +206,13 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage } override fun onError(id: String, responseModel: ResponseModel) { - val response = Try.failure(ResponseErrorException( + val response = Try.failure( + ResponseErrorException( responseModel.statusCode, responseModel.message, - responseModel.body)) + responseModel.body + ) + ) resultListener.onResult(response) } @@ -221,10 +235,13 @@ class DefaultConfigInternal(private val mobileEngageRequestContext: MobileEngage } override fun onError(id: String, responseModel: ResponseModel) { - val response = Try.failure(ResponseErrorException( + val response = Try.failure( + ResponseErrorException( responseModel.statusCode, responseModel.message, - responseModel.body)) + responseModel.body + ) + ) resultListener.onResult(response) } diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternalTest.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternalTest.kt index 55410f4ae..d1b8228f3 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternalTest.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternalTest.kt @@ -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 { @@ -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) @@ -144,4 +135,16 @@ class MobileEngageRefreshTokenInternalTest { latch.await() verify(mockCompletionListener).onCompleted(any()) } + + @Test + fun testRefreshContactToken_shouldCallCompletionListener_whenRequestModelFactoryThrowsIllegalArgumentException() { + val mockCompletionListener: CompletionListener = mock() + whenever(mockRequestModelFactory.createRefreshContactTokenRequest()).thenThrow( + IllegalArgumentException("") + ) + + refreshTokenInternal.refreshContactToken(mockCompletionListener) + + verify(mockCompletionListener).onCompleted(any()) + } } \ No newline at end of file diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/fake/FakeMobileEngageRefreshTokenInternal.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/fake/FakeMobileEngageRefreshTokenInternal.kt index 46ff5cbbf..95ac99ab3 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/fake/FakeMobileEngageRefreshTokenInternal.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/fake/FakeMobileEngageRefreshTokenInternal.kt @@ -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()) } } } diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/session/MobileEngageSessionTest.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/session/MobileEngageSessionTest.kt index c593ab6b5..181202bf5 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/session/MobileEngageSessionTest.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/session/MobileEngageSessionTest.kt @@ -3,12 +3,18 @@ package com.emarsys.mobileengage.session import com.emarsys.core.provider.timestamp.TimestampProvider import com.emarsys.core.provider.uuid.UUIDProvider import com.emarsys.core.storage.Storage +import com.emarsys.mobileengage.MobileEngageRequestContext import com.emarsys.mobileengage.event.EventServiceInternal -import io.kotlintest.shouldBe -import io.kotlintest.shouldThrow import org.junit.Before import org.junit.Test -import org.mockito.kotlin.* +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.isNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import org.mockito.kotlin.whenever class MobileEngageSessionTest { @@ -22,9 +28,13 @@ class MobileEngageSessionTest { private lateinit var mockSessionIdHolder: SessionIdHolder private lateinit var session: MobileEngageSession private lateinit var mockContactTokenStorage: Storage + private lateinit var mockMobileEngageRequestContext: MobileEngageRequestContext @Before fun setUp() { + mockMobileEngageRequestContext = mock { + on { applicationCode } doReturn "testApplicationCode" + } mockContactTokenStorage = mock { on { get() } doReturn "testContactToken" } @@ -38,7 +48,14 @@ class MobileEngageSessionTest { mockSessionIdHolder = mock { on { sessionId } doReturn SESSION_ID } - session = MobileEngageSession(mockTimestampProvider, mockUUIDProvider, mockEventServiceInternal, mockSessionIdHolder, mockContactTokenStorage) + session = MobileEngageSession( + mockTimestampProvider, + mockUUIDProvider, + mockEventServiceInternal, + mockSessionIdHolder, + mockContactTokenStorage, + mockMobileEngageRequestContext + ) } @Test @@ -67,11 +84,27 @@ class MobileEngageSessionTest { verifyNoInteractions(mockEventServiceInternal) } + @Test + fun testStartSession_doesNothing_whenApplicationCodeIsMissing() { + whenever(mockContactTokenStorage.get()) doReturn null + whenever(mockMobileEngageRequestContext.applicationCode) doReturn null + + session.startSession {} + + verifyNoInteractions(mockUUIDProvider) + verifyNoInteractions(mockTimestampProvider) + verifyNoInteractions(mockEventServiceInternal) + } + @Test fun testStartSession_reportSessionStartToEventServiceInternal_byInternalCustomEvent() { session.startSession {} - verify(mockEventServiceInternal).trackInternalCustomEventAsync(eq("session:start"), isNull(), anyOrNull()) + verify(mockEventServiceInternal).trackInternalCustomEventAsync( + eq("session:start"), + isNull(), + anyOrNull() + ) } @Test @@ -79,9 +112,13 @@ class MobileEngageSessionTest { session.startSession {} session.endSession {} - verify(mockEventServiceInternal).trackInternalCustomEventAsync(eq("session:end"), eq(mapOf( - "duration" to "1" - )), anyOrNull()) + verify(mockEventServiceInternal).trackInternalCustomEventAsync( + eq("session:end"), eq( + mapOf( + "duration" to "1" + ) + ), anyOrNull() + ) } @Test @@ -91,4 +128,15 @@ class MobileEngageSessionTest { verify(mockSessionIdHolder).sessionId = null } + + @Test + fun testEndSession_shouldDoNothingWhenApplicationCodeIsMissing() { + whenever(mockMobileEngageRequestContext.applicationCode) doReturn "testAppCode" + + session.startSession {} + whenever(mockMobileEngageRequestContext.applicationCode) doReturn null + session.endSession {} + + verify(mockSessionIdHolder).sessionId != null + } } \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt index c11f98d93..6296bdd22 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt @@ -11,17 +11,17 @@ import com.emarsys.mobileengage.session.SessionIdHolder @Mockable class DefaultMobileEngageInternal( - private val requestManager: RequestManager, - private val requestModelFactory: MobileEngageRequestModelFactory, - private val requestContext: MobileEngageRequestContext, - private val session: MobileEngageSession, - private val sessionIdHolder: SessionIdHolder + private val requestManager: RequestManager, + private val requestModelFactory: MobileEngageRequestModelFactory, + private val requestContext: MobileEngageRequestContext, + private val session: MobileEngageSession, + private val sessionIdHolder: SessionIdHolder ) : MobileEngageInternal { override fun setContact( - contactFieldId: Int?, - contactFieldValue: String?, - completionListener: CompletionListener? + contactFieldId: Int?, + contactFieldValue: String?, + completionListener: CompletionListener? ) { val shouldRestartSession = requestContext.contactFieldValue != contactFieldValue doSetContact(contactFieldId, contactFieldValue, completionListener = completionListener) @@ -43,9 +43,9 @@ class DefaultMobileEngageInternal( } override fun setAuthenticatedContact( - contactFieldId: Int, - openIdToken: String, - completionListener: CompletionListener? + contactFieldId: Int, + openIdToken: String, + completionListener: CompletionListener? ) { val shouldRestartSession = requestContext.openIdToken != openIdToken doSetContact(contactFieldId, null, openIdToken, completionListener) @@ -67,17 +67,21 @@ class DefaultMobileEngageInternal( } internal fun doSetContact( - contactFieldId: Int?, - contactFieldValue: String?, - idToken: String? = null, - completionListener: CompletionListener? + contactFieldId: Int?, + contactFieldValue: String?, + idToken: String? = null, + completionListener: CompletionListener? ) { requestContext.contactFieldId = contactFieldId requestContext.contactFieldValue = contactFieldValue requestContext.openIdToken = idToken - val requestModel = + try { + val requestModel = requestModelFactory.createSetContactRequest(contactFieldId, contactFieldValue) - requestManager.submit(requestModel, completionListener) + requestManager.submit(requestModel, completionListener) + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + } } override fun clearContact(completionListener: CompletionListener?) { @@ -93,7 +97,7 @@ class DefaultMobileEngageInternal( } } - internal fun doClearContact(completionListener: CompletionListener?) { + internal fun doClearContact(completionListener: CompletionListener?) { resetContext() doSetContact(null, null, null) { setContactThrowable -> diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.java deleted file mode 100644 index a2fda8ea5..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.emarsys.mobileengage; - -import com.emarsys.core.CoreCompletionHandler; -import com.emarsys.core.api.ResponseErrorException; -import com.emarsys.core.api.result.CompletionListener; -import com.emarsys.core.request.RestClient; -import com.emarsys.core.request.model.RequestModel; -import com.emarsys.core.response.ResponseModel; -import com.emarsys.core.util.Assert; -import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory; -import com.emarsys.mobileengage.responsehandler.MobileEngageTokenResponseHandler; - -public class MobileEngageRefreshTokenInternal implements RefreshTokenInternal { - MobileEngageTokenResponseHandler tokenResponseHandler; - private final RestClient restClient; - private final MobileEngageRequestModelFactory requestModelFactory; - - public MobileEngageRefreshTokenInternal(MobileEngageTokenResponseHandler tokenResponseHandler, RestClient restClient, MobileEngageRequestModelFactory requestModelFactory) { - Assert.notNull(tokenResponseHandler, "TokenResponseHandler must not be null!"); - Assert.notNull(restClient, "RestClient must not be null!"); - Assert.notNull(requestModelFactory, "RequestModelFactory must not be null!"); - this.tokenResponseHandler = tokenResponseHandler; - this.restClient = restClient; - this.requestModelFactory = requestModelFactory; - } - - @Override - public void refreshContactToken(final CompletionListener completionListener) { - final RequestModel requestModel = requestModelFactory.createRefreshContactTokenRequest(); - restClient.execute(requestModel, new CoreCompletionHandler() { - @Override - public void onSuccess(String id, ResponseModel responseModel) { - tokenResponseHandler.processResponse(responseModel); - completionListener.onCompleted(null); - } - - @Override - public void onError(String id, ResponseModel responseModel) { - completionListener.onCompleted(new ResponseErrorException( - responseModel.getStatusCode(), - responseModel.getMessage(), - responseModel.getBody())); - } - - @Override - public void onError(String id, Exception cause) { - completionListener.onCompleted(cause); - } - }); - } -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.kt new file mode 100644 index 000000000..f16aadb93 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/MobileEngageRefreshTokenInternal.kt @@ -0,0 +1,45 @@ +package com.emarsys.mobileengage + +import com.emarsys.core.CoreCompletionHandler +import com.emarsys.core.api.ResponseErrorException +import com.emarsys.core.api.result.CompletionListener +import com.emarsys.core.request.RestClient +import com.emarsys.core.response.ResponseModel +import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory +import com.emarsys.mobileengage.responsehandler.MobileEngageTokenResponseHandler + +class MobileEngageRefreshTokenInternal( + private var tokenResponseHandler: MobileEngageTokenResponseHandler, + private val restClient: RestClient, + private val requestModelFactory: MobileEngageRequestModelFactory +) : RefreshTokenInternal { + + override fun refreshContactToken(completionListener: CompletionListener?) { + try { + val requestModel = requestModelFactory.createRefreshContactTokenRequest() + restClient.execute(requestModel, object : CoreCompletionHandler { + override fun onSuccess(id: String, responseModel: ResponseModel) { + tokenResponseHandler.processResponse(responseModel) + completionListener?.onCompleted(null) + } + + override fun onError(id: String, responseModel: ResponseModel) { + completionListener?.onCompleted( + ResponseErrorException( + responseModel.statusCode, + responseModel.message, + responseModel.body + ) + ) + } + + override fun onError(id: String, cause: Exception) { + completionListener?.onCompleted(cause) + } + }) + + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + } + } +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.java deleted file mode 100644 index db693329a..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.emarsys.mobileengage; - -import com.emarsys.core.api.result.CompletionListener; - -public interface RefreshTokenInternal { - - void refreshContactToken(CompletionListener completionListener); -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.kt new file mode 100644 index 000000000..1b7b470e3 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/RefreshTokenInternal.kt @@ -0,0 +1,7 @@ +package com.emarsys.mobileengage + +import com.emarsys.core.api.result.CompletionListener + +interface RefreshTokenInternal { + fun refreshContactToken(completionListener: CompletionListener?) +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.java deleted file mode 100644 index ad100157f..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.emarsys.mobileengage.client; - -import com.emarsys.core.api.result.CompletionListener; - -public interface ClientServiceInternal { - - void trackDeviceInfo(CompletionListener completionListener); -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.kt new file mode 100644 index 000000000..b87dd14c0 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/ClientServiceInternal.kt @@ -0,0 +1,7 @@ +package com.emarsys.mobileengage.client + +import com.emarsys.core.api.result.CompletionListener + +interface ClientServiceInternal { + fun trackDeviceInfo(completionListener: CompletionListener?) +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.java deleted file mode 100644 index 951f2b1e7..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.emarsys.mobileengage.client; - -import com.emarsys.core.api.result.CompletionListener; -import com.emarsys.core.request.RequestManager; -import com.emarsys.core.request.model.RequestModel; -import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory; - -public class DefaultClientServiceInternal implements ClientServiceInternal { - private final MobileEngageRequestModelFactory requestModelFactory; - private final RequestManager requestManager; - - public DefaultClientServiceInternal(RequestManager requestManager, MobileEngageRequestModelFactory requestModelFactory) { - this.requestModelFactory = requestModelFactory; - this.requestManager = requestManager; - } - - @Override - public void trackDeviceInfo(CompletionListener completionListener) { - RequestModel requestModel = requestModelFactory.createTrackDeviceInfoRequest(); - - requestManager.submit(requestModel, completionListener); - } -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.kt new file mode 100644 index 000000000..6c29ded58 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/DefaultClientServiceInternal.kt @@ -0,0 +1,19 @@ +package com.emarsys.mobileengage.client + +import com.emarsys.core.api.result.CompletionListener +import com.emarsys.core.request.RequestManager +import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory + +class DefaultClientServiceInternal( + private val requestManager: RequestManager, + private val requestModelFactory: MobileEngageRequestModelFactory +) : ClientServiceInternal { + override fun trackDeviceInfo(completionListener: CompletionListener?) { + try { + val requestModel = requestModelFactory.createTrackDeviceInfoRequest() + requestManager.submit(requestModel, completionListener) + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + } + } +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/LoggingClientServiceInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/LoggingClientServiceInternal.kt index 1de955e04..b3a612858 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/client/LoggingClientServiceInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/client/LoggingClientServiceInternal.kt @@ -6,7 +6,7 @@ import com.emarsys.core.util.log.Logger.Companion.debug import com.emarsys.core.util.log.entry.MethodNotAllowed class LoggingClientServiceInternal(private val klass: Class<*>) : ClientServiceInternal { - override fun trackDeviceInfo(completionListener: CompletionListener) { + override fun trackDeviceInfo(completionListener: CompletionListener?) { val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, null)) } diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.java deleted file mode 100644 index 80ebc2742..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.emarsys.mobileengage.endpoint; - -import com.emarsys.common.feature.InnerFeature; -import com.emarsys.core.feature.FeatureRegistry; -import com.emarsys.mobileengage.BuildConfig; - -public class Endpoint { - public static final String DEEP_LINK = "https://deep-link.eservice.emarsys.net"; - - public static final String ME_CLIENT_HOST = "https://me-client.eservice.emarsys.net"; - - public static final String ME_EVENT_HOST = "https://mobile-events.eservice.emarsys.net"; - - public static final String ME_V3_INBOX_HOST = "https://me-inbox.eservice.emarsys.net/v3"; - - public static String deepLinkBase(){ - return "/api/clicks"; - } - - public static String clientBase(String applicationCode) { - return "/v3/apps/" + applicationCode + "/client"; - } - - public static String eventBase(String applicationCode) { - String version = "v3"; - if (FeatureRegistry.isFeatureEnabled(InnerFeature.EVENT_SERVICE_V4)) { - version = "v4"; - } - return "/" + version + "/apps/" + applicationCode + "/client/events"; - } - - public static String inboxBase(String applicationCode) { - return "/apps/" + applicationCode + "/inbox"; - } - - public static String geofencesBase(String applicationCode) { - return "/v3/apps/" + applicationCode + "/geo-fences"; - } - - public static String inlineInAppBase(String applicationCode) { - String version = "v3"; - if (FeatureRegistry.isFeatureEnabled(InnerFeature.EVENT_SERVICE_V4)) { - version = "v4"; - } - return "/" + version + "/apps/" + applicationCode + "/inline-messages"; - } -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.kt new file mode 100644 index 000000000..4dda0cc51 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/endpoint/Endpoint.kt @@ -0,0 +1,42 @@ +package com.emarsys.mobileengage.endpoint + +import com.emarsys.common.feature.InnerFeature +import com.emarsys.core.feature.FeatureRegistry.isFeatureEnabled + +object Endpoint { + const val DEEP_LINK = "https://deep-link.eservice.emarsys.net" + const val ME_CLIENT_HOST = "https://me-client.eservice.emarsys.net" + const val ME_EVENT_HOST = "https://mobile-events.eservice.emarsys.net" + const val ME_V3_INBOX_HOST = "https://me-inbox.eservice.emarsys.net/v3" + fun deepLinkBase(): String { + return "/api/clicks" + } + + fun clientBase(applicationCode: String): String { + return "/v3/apps/$applicationCode/client" + } + + fun eventBase(applicationCode: String): String { + var version = "v3" + if (isFeatureEnabled(InnerFeature.EVENT_SERVICE_V4)) { + version = "v4" + } + return "/$version/apps/$applicationCode/client/events" + } + + fun inboxBase(applicationCode: String): String { + return "/apps/$applicationCode/inbox" + } + + fun geofencesBase(applicationCode: String): String { + return "/v3/apps/$applicationCode/geo-fences" + } + + fun inlineInAppBase(applicationCode: String): String { + var version = "v3" + if (isFeatureEnabled(InnerFeature.EVENT_SERVICE_V4)) { + version = "v4" + } + return "/$version/apps/$applicationCode/inline-messages" + } +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/event/DefaultEventServiceInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/event/DefaultEventServiceInternal.kt index 81d8a4a54..6ef44bacc 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/event/DefaultEventServiceInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/event/DefaultEventServiceInternal.kt @@ -9,7 +9,8 @@ import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory @Mockable class DefaultEventServiceInternal( private val requestModelFactory: MobileEngageRequestModelFactory, - private val requestManager: RequestManager): EventServiceInternal { + private val requestManager: RequestManager +) : EventServiceInternal { override fun trackCustomEvent( eventName: String, @@ -17,9 +18,16 @@ class DefaultEventServiceInternal( completionListener: CompletionListener? ): String? { Assert.notNull(eventName, "EventName must not be null!") - val requestModel = requestModelFactory.createCustomEventRequest(eventName, eventAttributes) - requestManager.submit(requestModel, completionListener) - return requestModel.id + val requestId = try { + val requestModel = + requestModelFactory.createCustomEventRequest(eventName, eventAttributes) + requestManager.submit(requestModel, completionListener) + requestModel.id + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + null + } + return requestId } override fun trackCustomEventAsync( @@ -36,10 +44,16 @@ class DefaultEventServiceInternal( completionListener: CompletionListener? ): String? { Assert.notNull(eventName, "EventName must not be null!") - val requestModel = - requestModelFactory.createInternalCustomEventRequest(eventName, eventAttributes) - requestManager.submit(requestModel, completionListener) - return requestModel.id + val requestId = try { + val requestModel = + requestModelFactory.createInternalCustomEventRequest(eventName, eventAttributes) + requestManager.submit(requestModel, completionListener) + requestModel.id + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + null + } + return requestId } override fun trackInternalCustomEventAsync( diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/DefaultGeofenceInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/DefaultGeofenceInternal.kt index f9be03f2e..c00bfee94 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/DefaultGeofenceInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/DefaultGeofenceInternal.kt @@ -77,19 +77,24 @@ class DefaultGeofenceInternal( if (!geofenceEnabledStorage.get()) { return } - val requestModel = requestModelFactory.createFetchGeofenceRequest() - requestManager.submitNow(requestModel, object : CoreCompletionHandler { - override fun onSuccess(id: String, responseModel: ResponseModel) { - geofenceResponse = geofenceResponseMapper.map(responseModel) - enable(completionListener) - } + try { - override fun onError(id: String, responseModel: ResponseModel) { - } + val requestModel = requestModelFactory.createFetchGeofenceRequest() + requestManager.submitNow(requestModel, object : CoreCompletionHandler { + override fun onSuccess(id: String, responseModel: ResponseModel) { + geofenceResponse = geofenceResponseMapper.map(responseModel) + enable(completionListener) + } - override fun onError(id: String, cause: Exception) { - } - }) + override fun onError(id: String, responseModel: ResponseModel) { + } + + override fun onError(id: String, cause: Exception) { + } + }) + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + } } override fun enable(completionListener: CompletionListener?) { @@ -191,13 +196,14 @@ class DefaultGeofenceInternal( } private fun requestLocationUpdate(): Task { - val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, FASTEST_INTERNAL) - .setMaxUpdateAgeMillis(MAX_WAIT_TIME) - .setIntervalMillis(INTERVAL) - .setMinUpdateDistanceMeters(5f) - .setGranularity(Granularity.GRANULARITY_FINE) - .setWaitForAccurateLocation(true) - .build() + val locationRequest = + LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, FASTEST_INTERNAL) + .setMaxUpdateAgeMillis(MAX_WAIT_TIME) + .setIntervalMillis(INTERVAL) + .setMinUpdateDistanceMeters(5f) + .setGranularity(Granularity.GRANULARITY_FINE) + .setWaitForAccurateLocation(true) + .build() return fusedLocationProviderClient.requestLocationUpdates( locationRequest, geofencePendingIntent diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/inbox/DefaultMessageInboxInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/inbox/DefaultMessageInboxInternal.kt index f98c2cb58..c350350c2 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/inbox/DefaultMessageInboxInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/inbox/DefaultMessageInboxInternal.kt @@ -12,7 +12,7 @@ import com.emarsys.core.response.ResponseModel import com.emarsys.mobileengage.api.inbox.InboxResult import com.emarsys.mobileengage.api.inbox.Message import com.emarsys.mobileengage.request.MobileEngageRequestModelFactory -import java.util.* +import java.util.Locale class DefaultMessageInboxInternal( private val concurrentHandlerHolder: ConcurrentHandlerHolder, @@ -66,7 +66,11 @@ class DefaultMessageInboxInternal( predicate(it) } ?: true).let { shouldUpdateTag -> if (shouldUpdateTag) { - requestManager.submit(updateRequest(), completionListener) + try { + requestManager.submit(updateRequest(), completionListener) + } catch (e: IllegalArgumentException) { + completionListener?.onCompleted(e) + } } else { completionListener?.onCompleted(null) } diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/DefaultPushInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/DefaultPushInternal.kt index 677992ffc..61c882708 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/DefaultPushInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/DefaultPushInternal.kt @@ -42,9 +42,8 @@ class DefaultPushInternal( } } - override fun getPushToken(): String? { - return pushTokenStorage.get() - } + override val pushToken: String? + get() = pushTokenStorage.get() override fun setNotificationInformationListener(notificationInformationListener: NotificationInformationListener) { notificationInformationListenerProvider.notificationInformationListener = diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/LoggingPushInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/LoggingPushInternal.kt index db4347466..1a8f4d769 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/LoggingPushInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/LoggingPushInternal.kt @@ -11,22 +11,23 @@ import com.emarsys.mobileengage.api.push.NotificationInformationListener class LoggingPushInternal(private val klass: Class<*>) : PushInternal { override fun setPushToken(pushToken: String, completionListener: CompletionListener?) { val parameters = mapOf( - "push_token" to pushToken, - "completion_listener" to (completionListener != null) + "push_token" to pushToken, + "completion_listener" to (completionListener != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) } - override fun getPushToken(): String? { - val callerMethodName = SystemUtils.getCallerMethodName() - debug(MethodNotAllowed(klass, callerMethodName, null)) - return null - } + override val pushToken: String? + get() { + val callerMethodName = SystemUtils.getCallerMethodName() + debug(MethodNotAllowed(klass, callerMethodName, null)) + return null + } override fun clearPushToken(completionListener: CompletionListener?) { val parameters: Map = mapOf( - "completion_listener" to (completionListener != null) + "completion_listener" to (completionListener != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) @@ -34,41 +35,41 @@ class LoggingPushInternal(private val klass: Class<*>) : PushInternal { override fun trackMessageOpen(intent: Intent, completionListener: CompletionListener?) { val parameters = mapOf( - "intent" to intent.toString(), - "completion_listener" to (completionListener != null) + "intent" to intent.toString(), + "completion_listener" to (completionListener != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) } - override fun setNotificationEventHandler(notificationEventHandler: EventHandler?) { + override fun setNotificationEventHandler(notificationEventHandler: EventHandler) { val parameters: Map = mapOf( - "notification_event_handler" to (notificationEventHandler != null) + "notification_event_handler" to (notificationEventHandler != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) } - override fun setSilentMessageEventHandler(silentMessageEventHandler: EventHandler?) { + override fun setSilentMessageEventHandler(silentMessageEventHandler: EventHandler) { val parameters: Map = mapOf( - "silent_message_event_handler" to (silentMessageEventHandler != null) + "silent_message_event_handler" to (silentMessageEventHandler != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) } - override fun setNotificationInformationListener(notificationInformationListener: NotificationInformationListener?) { + override fun setNotificationInformationListener(notificationInformationListener: NotificationInformationListener) { val parameters: Map = mapOf( - "notification_information_listener" to (notificationInformationListener != null) + "notification_information_listener" to (notificationInformationListener != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) } - override fun setSilentNotificationInformationListener(silentNotificationInformationListener: NotificationInformationListener?) { + override fun setSilentNotificationInformationListener(silentNotificationInformationListener: NotificationInformationListener) { val parameters: Map = mapOf( - "notification_information_listener" to (silentNotificationInformationListener != null) + "notification_information_listener" to (silentNotificationInformationListener != null) ) val callerMethodName = SystemUtils.getCallerMethodName() debug(MethodNotAllowed(klass, callerMethodName, parameters)) diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.java b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.java deleted file mode 100644 index 6c0ff46f7..000000000 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.emarsys.mobileengage.push; - -import android.content.Intent; - -import com.emarsys.core.api.result.CompletionListener; -import com.emarsys.mobileengage.api.event.EventHandler; -import com.emarsys.mobileengage.api.push.NotificationInformationListener; - -public interface PushInternal { - void setPushToken(String pushToken, CompletionListener completionListener); - - String getPushToken(); - - void clearPushToken(CompletionListener completionListener); - - void trackMessageOpen(Intent intent, CompletionListener completionListener); - - void setNotificationEventHandler(EventHandler notificationEventHandler); - - void setSilentMessageEventHandler(EventHandler silentMessageEventHandler); - - void setNotificationInformationListener(NotificationInformationListener notificationInformationListener); - - void setSilentNotificationInformationListener(NotificationInformationListener silentNotificationInformationListener); -} diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.kt new file mode 100644 index 000000000..66fb06a50 --- /dev/null +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/push/PushInternal.kt @@ -0,0 +1,17 @@ +package com.emarsys.mobileengage.push + +import android.content.Intent +import com.emarsys.core.api.result.CompletionListener +import com.emarsys.mobileengage.api.event.EventHandler +import com.emarsys.mobileengage.api.push.NotificationInformationListener + +interface PushInternal { + fun setPushToken(pushToken: String, completionListener: CompletionListener?) + val pushToken: String? + fun clearPushToken(completionListener: CompletionListener?) + fun trackMessageOpen(intent: Intent, completionListener: CompletionListener?) + fun setNotificationEventHandler(notificationEventHandler: EventHandler) + fun setSilentMessageEventHandler(silentMessageEventHandler: EventHandler) + fun setNotificationInformationListener(notificationInformationListener: NotificationInformationListener) + fun setSilentNotificationInformationListener(silentNotificationInformationListener: NotificationInformationListener) +} \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt index 652112871..d9a2e1442 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt @@ -16,41 +16,84 @@ import com.emarsys.mobileengage.util.RequestPayloadUtils.createInternalCustomEve import com.emarsys.mobileengage.util.RequestPayloadUtils.createRefreshContactTokenPayload import com.emarsys.mobileengage.util.RequestPayloadUtils.createSetPushTokenPayload import com.emarsys.mobileengage.util.RequestPayloadUtils.createTrackDeviceInfoPayload -import java.util.* @Mockable -class MobileEngageRequestModelFactory(private val requestContext: MobileEngageRequestContext, - private val clientServiceProvider: ServiceEndpointProvider, - private val eventServiceProvider: ServiceEndpointProvider, - private val messageInboxServiceProvider: ServiceEndpointProvider, - private val buttonClickedRepository: Repository) { +class MobileEngageRequestModelFactory( + private val requestContext: MobileEngageRequestContext, + private val clientServiceProvider: ServiceEndpointProvider, + private val eventServiceProvider: ServiceEndpointProvider, + private val messageInboxServiceProvider: ServiceEndpointProvider, + private val buttonClickedRepository: Repository +) { fun createSetPushTokenRequest(pushToken: String): RequestModel { + val applicationCode = validateApplicationCode() return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.clientBase(requestContext.applicationCode)}/push-token") - .method(RequestMethod.PUT) - .payload(createSetPushTokenPayload(pushToken)) - .build() + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.clientBase( + applicationCode + ) + }/push-token" + ) + .method(RequestMethod.PUT) + .payload(createSetPushTokenPayload(pushToken)) + .build() + } + + private fun validateApplicationCode(): String { + return if (requestContext.applicationCode.isNullOrBlank()) { + throw IllegalArgumentException( + "Application Code must not be null!" + ) + } else ({ + requestContext.applicationCode + })!! } fun createRemovePushTokenRequest(): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.clientBase(requestContext.applicationCode)}/push-token") - .method(RequestMethod.DELETE) - .build() + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.clientBase( + applicationCode + ) + }/push-token" + ) + .method(RequestMethod.DELETE) + .build() } fun createTrackDeviceInfoRequest(): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.clientBase(requestContext.applicationCode)}") - .method(RequestMethod.POST) - .payload(createTrackDeviceInfoPayload(requestContext)) - .build() + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.clientBase( + applicationCode + ) + }" + ) + .method(RequestMethod.POST) + .payload(createTrackDeviceInfoPayload(requestContext)) + .build() } fun createSetContactRequest(contactFieldId: Int?, contactFieldValue: String?): RequestModel { - val builder = RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.clientBase(requestContext.applicationCode)}/contact") + val applicationCode = validateApplicationCode() + + val builder = + RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.clientBase( + applicationCode + ) + }/contact" + ) .method(RequestMethod.POST) if (!requestContext.hasContactIdentification()) { @@ -71,51 +114,105 @@ class MobileEngageRequestModelFactory(private val requestContext: MobileEngageRe return builder.build() } - fun createCustomEventRequest(eventName: String, eventAttributes: Map?): RequestModel { + fun createCustomEventRequest( + eventName: String, + eventAttributes: Map? + ): RequestModel { val payload = createCustomEventPayload(eventName, eventAttributes, requestContext) return createEvent(payload, requestContext) } - fun createInternalCustomEventRequest(eventName: String, eventAttributes: Map?): RequestModel { + fun createInternalCustomEventRequest( + eventName: String, + eventAttributes: Map? + ): RequestModel { val payload = createInternalCustomEventPayload(eventName, eventAttributes, requestContext) return createEvent(payload, requestContext) } fun createRefreshContactTokenRequest(): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.clientBase(requestContext.applicationCode)}/contact-token") - .method(RequestMethod.POST) - .payload(createRefreshContactTokenPayload(requestContext)) - .build() + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.clientBase( + applicationCode + ) + }/contact-token" + ) + .method(RequestMethod.POST) + .payload(createRefreshContactTokenPayload(requestContext)) + .build() } - private fun createEvent(payload: Map, requestContext: MobileEngageRequestContext): RequestModel { + private fun createEvent( + payload: Map, + requestContext: MobileEngageRequestContext + ): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .url("${eventServiceProvider.provideEndpointHost()}${Endpoint.eventBase(requestContext.applicationCode)}") - .method(RequestMethod.POST) - .payload(payload) - .build() + .url( + "${eventServiceProvider.provideEndpointHost()}${ + Endpoint.eventBase( + applicationCode + ) + }" + ) + .method(RequestMethod.POST) + .payload(payload) + .build() } fun createFetchInboxMessagesRequest(): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .method(RequestMethod.GET) - .url("${messageInboxServiceProvider.provideEndpointHost()}${Endpoint.inboxBase(requestContext.applicationCode)}") - .build() + .method(RequestMethod.GET) + .url( + "${messageInboxServiceProvider.provideEndpointHost()}${ + Endpoint.inboxBase( + applicationCode + ) + }" + ) + .build() } fun createFetchGeofenceRequest(): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .method(RequestMethod.GET) - .url("${clientServiceProvider.provideEndpointHost()}${Endpoint.geofencesBase(requestContext.applicationCode)}") - .build() + .method(RequestMethod.GET) + .url( + "${clientServiceProvider.provideEndpointHost()}${ + Endpoint.geofencesBase( + applicationCode + ) + }" + ) + .build() } fun createFetchInlineInAppMessagesRequest(viewId: String): RequestModel { + val applicationCode = validateApplicationCode() + return RequestModel.Builder(requestContext.timestampProvider, requestContext.uuidProvider) - .method(RequestMethod.POST) - .payload(RequestPayloadUtils.createInlineInAppPayload(viewId, buttonClickedRepository.query(Everything()))) - .url("${eventServiceProvider.provideEndpointHost()}${Endpoint.inlineInAppBase(requestContext.applicationCode)}") - .build() + .method(RequestMethod.POST) + .payload( + RequestPayloadUtils.createInlineInAppPayload( + viewId, + buttonClickedRepository.query(Everything()) + ) + ) + .url( + "${eventServiceProvider.provideEndpointHost()}${ + Endpoint.inlineInAppBase( + applicationCode + ) + }" + ) + .build() } } \ No newline at end of file diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/session/MobileEngageSession.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/session/MobileEngageSession.kt index b4e3c81a7..f67f8d190 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/session/MobileEngageSession.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/session/MobileEngageSession.kt @@ -8,38 +8,59 @@ import com.emarsys.core.session.Session import com.emarsys.core.storage.Storage import com.emarsys.core.util.log.Logger import com.emarsys.core.util.log.entry.StatusLog +import com.emarsys.mobileengage.MobileEngageRequestContext import com.emarsys.mobileengage.event.EventServiceInternal @Mockable -class MobileEngageSession(private val timestampProvider: TimestampProvider, - private val uuidProvider: UUIDProvider, - private val eventServiceInternal: EventServiceInternal, - private val sessionIdHolder: SessionIdHolder, - private val contactTokenStorage: Storage) : Session { +class MobileEngageSession( + private val timestampProvider: TimestampProvider, + private val uuidProvider: UUIDProvider, + private val eventServiceInternal: EventServiceInternal, + private val sessionIdHolder: SessionIdHolder, + private val contactTokenStorage: Storage, + private val mobileEngageRequestContext: MobileEngageRequestContext +) : Session { private var sessionStart: Long? = null override fun startSession(completionListener: CompletionListener) { - if (!contactTokenStorage.get().isNullOrEmpty()) { + if (!contactTokenStorage.get() + .isNullOrEmpty() && mobileEngageRequestContext.applicationCode != null + ) { sessionIdHolder.sessionId = uuidProvider.provideId() sessionStart = timestampProvider.provideTimestamp() - eventServiceInternal.trackInternalCustomEventAsync("session:start", null, completionListener) + eventServiceInternal.trackInternalCustomEventAsync( + "session:start", + null, + completionListener + ) } } override fun endSession(completionListener: CompletionListener) { - if (sessionIdHolder.sessionId != null && sessionStart != null) { + if (sessionIdHolder.sessionId != null && sessionStart != null && mobileEngageRequestContext.applicationCode != null) { val sessionEnd = (timestampProvider.provideTimestamp() - sessionStart!!).toString() val attributes = mapOf( - "duration" to sessionEnd + "duration" to sessionEnd + ) + eventServiceInternal.trackInternalCustomEventAsync( + "session:end", + attributes, + completionListener ) - eventServiceInternal.trackInternalCustomEventAsync("session:end", attributes, completionListener) sessionIdHolder.sessionId = null sessionStart = null } else { if (!contactTokenStorage.get().isNullOrEmpty()) { - Logger.info(StatusLog(this::class.java, "endSession", parameters = null, status = mapOf("cause" to "StartSession has to be called first!"))) + Logger.info( + StatusLog( + this::class.java, + "endSession", + parameters = null, + status = mapOf("cause" to "StartSession has to be called first!") + ) + ) } } } diff --git a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt index 9b9694c6b..5dbeb3748 100644 --- a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt +++ b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt @@ -28,7 +28,7 @@ class SampleApplication : Application(), EventHandler, NotificationInformationLi val config = EmarsysConfig( application = this, - applicationCode = if (Prefs.applicationCode.isEmpty()) null else Prefs.applicationCode, + applicationCode = null, //if (Prefs.applicationCode.isEmpty()) null else Prefs.applicationCode, merchantId = Prefs.merchantId, verboseConsoleLoggingEnabled = true ) From 76f5fe5231d8c1c92d9a62a9a8c933e1c12cf417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyady=20Mih=C3=A1ly?= Date: Mon, 18 Sep 2023 15:41:51 +0200 Subject: [PATCH 2/3] feat(emarsys): Make requests null applicationCode proof SUITEDEV-34202 Co-authored-by: davidSchuppa <32750715+davidSchuppa@users.noreply.github.com> Co-authored-by: LasOri <24588073+LasOri@users.noreply.github.com> --- .../mobileengage/request/MobileEngageRequestModelFactory.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt index d9a2e1442..1e7a4b1a7 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/request/MobileEngageRequestModelFactory.kt @@ -46,9 +46,9 @@ class MobileEngageRequestModelFactory( throw IllegalArgumentException( "Application Code must not be null!" ) - } else ({ - requestContext.applicationCode - })!! + } else { + requestContext.applicationCode!! + } } fun createRemovePushTokenRequest(): RequestModel { From 915f946f844adb4d2ce525a49922cf7c142e2144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hunyady=20Mih=C3=A1ly?= Date: Tue, 19 Sep 2023 11:18:31 +0200 Subject: [PATCH 3/3] chore(release): 3.6.1 SUITEDEV-34202 Co-authored-by: davidSchuppa <32750715+davidSchuppa@users.noreply.github.com> Co-authored-by: LasOri <24588073+LasOri@users.noreply.github.com> --- changelog.md | 12 +++++------- .../kotlin/com/emarsys/sample/SampleApplication.kt | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index d251e0421..3479076cb 100644 --- a/changelog.md +++ b/changelog.md @@ -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 \ No newline at end of file +* Prevent requests without an application code from being sent and return an + InvalidArgumentException instead through the CompletionListener diff --git a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt index 5dbeb3748..9b9694c6b 100644 --- a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt +++ b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt @@ -28,7 +28,7 @@ class SampleApplication : Application(), EventHandler, NotificationInformationLi val config = EmarsysConfig( application = this, - applicationCode = null, //if (Prefs.applicationCode.isEmpty()) null else Prefs.applicationCode, + applicationCode = if (Prefs.applicationCode.isEmpty()) null else Prefs.applicationCode, merchantId = Prefs.merchantId, verboseConsoleLoggingEnabled = true )