From a34d79d1121205609c79ec1c685ee58ad9be3070 Mon Sep 17 00:00:00 2001 From: Andras Sarro Date: Wed, 25 Sep 2024 15:34:53 +0200 Subject: [PATCH 1/2] feat(shared-pref): make sharedPreferences a non-lazy property SUITEDEV-36635 Co-authored-by: megamegax --- .../java/com/emarsys/di/DefaultEmarsysComponent.kt | 14 +++++++------- .../kotlin/com/emarsys/sample/SampleApplication.kt | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) 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 472c0d8f..a0facfe6 100644 --- a/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt +++ b/emarsys-sdk/src/main/java/com/emarsys/di/DefaultEmarsysComponent.kt @@ -415,17 +415,17 @@ open class DefaultEmarsysComponent(config: EmarsysConfig) : EmarsysComponent { ) } - override val sharedPreferences: SharedPreferences by lazy { - val oldPrefs = config.application.getSharedPreferences( - EMARSYS_SHARED_PREFERENCES_NAME, - Context.MODE_PRIVATE - ) + private val oldSharedPrefs = config.application.getSharedPreferences( + EMARSYS_SHARED_PREFERENCES_NAME, + Context.MODE_PRIVATE + ) + + override val sharedPreferences: SharedPreferences = SecureSharedPreferencesProvider( config.application, EMARSYS_SECURE_SHARED_PREFERENCES_NAME, - oldPrefs + oldSharedPrefs ).provide() - } override val contactTokenStorage: Storage by lazy { StringStorage(MobileEngageStorageKey.CONTACT_TOKEN, sharedPreferences) diff --git a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt index 48a6078b..3a09df48 100644 --- a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt +++ b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt @@ -33,8 +33,11 @@ class SampleApplication : Application(), EventHandler, NotificationInformationLi verboseConsoleLoggingEnabled = true ) - Prefs.loggedIn = false Emarsys.setup(config) + Prefs.loggedIn = false + Prefs.hardwareId = Emarsys.config.hardwareId + Prefs.languageCode = Emarsys.config.languageCode + Prefs.sdkVersion = Emarsys.config.sdkVersion createNotificationChannels() setupEventHandlers() setContactIfPresent(context) From dd1716ac1b24d580b45f6d43d5701ba5da5c4cf8 Mon Sep 17 00:00:00 2001 From: megamegax Date: Mon, 30 Sep 2024 15:07:47 +0200 Subject: [PATCH 2/2] feat(contact): prevent sending duplicate setContact requests SUITEDEV-36670 Co-authored-by: LordAndras <49073629+LordAndras@users.noreply.github.com> Co-authored-by: matusekma <36794575+matusekma@users.noreply.github.com> --- .../DefaultMobileEngageInternalTest.kt | 77 +++++++++++++++---- .../DefaultMobileEngageInternal.kt | 22 ++++-- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/DefaultMobileEngageInternalTest.kt b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/DefaultMobileEngageInternalTest.kt index c7df9451..024e8e47 100644 --- a/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/DefaultMobileEngageInternalTest.kt +++ b/mobile-engage/src/androidTest/java/com/emarsys/mobileengage/DefaultMobileEngageInternalTest.kt @@ -14,6 +14,7 @@ import com.emarsys.mobileengage.session.MobileEngageSession import com.emarsys.mobileengage.session.SessionIdHolder import com.emarsys.testUtil.AnnotationSpec +import io.kotest.matchers.shouldBe import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.eq @@ -23,6 +24,7 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import java.util.concurrent.CountDownLatch @@ -128,43 +130,62 @@ class DefaultMobileEngageInternalTest : AnnotationSpec() { mockRequestModelFactory = mock { on { createSetContactRequest(CONTACT_FIELD_ID, null) }.thenReturn( - mockRequestModelWithNullContactFieldValue + mockRequestModelWithNullContactFieldValue ) on { createSetContactRequest(null, null) }.thenReturn( - mockRequestModelWithNullContactFieldValueAndNullContactFieldId + mockRequestModelWithNullContactFieldValueAndNullContactFieldId ) on { createSetContactRequest(CONTACT_FIELD_ID, CONTACT_FIELD_VALUE) }.thenReturn( - mockRequestModel + mockRequestModel ) on { createSetPushTokenRequest(PUSH_TOKEN) }.thenReturn(mockRequestModel) - on { createCustomEventRequest(EVENT_NAME, EVENT_ATTRIBUTES) }.thenReturn(mockRequestModel) + on { createCustomEventRequest(EVENT_NAME, EVENT_ATTRIBUTES) }.thenReturn( + mockRequestModel + ) on { createTrackDeviceInfoRequest() }.thenReturn(mockRequestModel) - on { createInternalCustomEventRequest(EVENT_NAME, EVENT_ATTRIBUTES) }.thenReturn(mockRequestModel) + on { createInternalCustomEventRequest(EVENT_NAME, EVENT_ATTRIBUTES) }.thenReturn( + mockRequestModel + ) on { createRemovePushTokenRequest() }.thenReturn(mockRequestModel) } mockCompletionListener = mock() mobileEngageInternal = DefaultMobileEngageInternal( - mockRequestManager, - mockRequestModelFactory, - mockRequestContext, - mockSession, - mockSessionIdHolder + mockRequestManager, + mockRequestModelFactory, + mockRequestContext, + mockSession, + mockSessionIdHolder ) } @Test - fun testSetContact() { + fun testSetContact_shouldCallRequestManager_whenNewSessionIsNeeded() { + whenever(mockRequestContext.contactFieldValue).thenReturn(OTHER_CONTACT_FIELD_VALUE) + mobileEngageInternal.setContact( - CONTACT_FIELD_ID, - CONTACT_FIELD_VALUE, - mockCompletionListener + CONTACT_FIELD_ID, + CONTACT_FIELD_VALUE, + mockCompletionListener ) verify(mockRequestManager).submit(mockRequestModel, mockCompletionListener) } + @Test + fun testSetContact_shouldNotCallRequestManager_whenSessionIsNotChanging() { + whenever(mockRequestContext.contactFieldValue).thenReturn(CONTACT_FIELD_VALUE) + + mobileEngageInternal.setContact( + CONTACT_FIELD_ID, + CONTACT_FIELD_VALUE, + mockCompletionListener + ) + + verifyNoInteractions(mockRequestManager) + } + @Test fun testSetAuthenticatedContact_completionListener_canBeNull() { mobileEngageInternal.setAuthenticatedContact(CONTACT_FIELD_ID, OPEN_ID_TOKEN, null) @@ -257,10 +278,15 @@ class DefaultMobileEngageInternalTest : AnnotationSpec() { verify(mockSession).endSession(any()) verify(mobileEngageInternal).doClearContact(any()) verify(mobileEngageInternal).resetContext() - verify(mobileEngageInternal).doSetContact(isNull(), isNull(), isNull(), any()) + verify(mobileEngageInternal).doSetContact( + isNull(), + isNull(), + isNull(), + any() + ) verify(mockRequestManager).submit( - eq(mockRequestModelWithNullContactFieldValueAndNullContactFieldId), - any() + eq(mockRequestModelWithNullContactFieldValueAndNullContactFieldId), + any() ) verifyNoMoreInteractions(mobileEngageInternal) verify(mockSession).startSession(any()) @@ -270,11 +296,28 @@ class DefaultMobileEngageInternalTest : AnnotationSpec() { @Test fun testClearContact_shouldEndCurrentSession() { whenever(mockSessionIdHolder.sessionId).thenReturn("testSessionId") + whenever(mockRequestContext.hasContactIdentification()).thenReturn(true) + whenever(mockRequestContext.contactTokenStorage.get()).thenReturn("contactToken") mobileEngageInternal.clearContact(null) verify(mockSession).endSession(any()) } + @Test + fun testClearContact_shouldCallOnCompleted_whenContactWasAlreadyAnonymous() { + whenever(mockRequestContext.hasContactIdentification()).thenReturn(false) + whenever(mockRequestContext.contactTokenStorage.get()).thenReturn("contactToken") + whenever(mockSessionIdHolder.sessionId).thenReturn("testSessionId") + + var result = false + mobileEngageInternal.clearContact { + result = true + } + result shouldBe true + verifyNoInteractions(mockSession) + verifyNoInteractions(mockRequestManager) + } + @Test fun testClearContact_shouldNotCallEndCurrentSession_whenThereWasNoSessionInProgress() { whenever(mockSessionIdHolder.sessionId).thenReturn(null) 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 6296bdd2..3ae0064c 100644 --- a/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt +++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/DefaultMobileEngageInternal.kt @@ -24,9 +24,9 @@ class DefaultMobileEngageInternal( completionListener: CompletionListener? ) { val shouldRestartSession = requestContext.contactFieldValue != contactFieldValue - doSetContact(contactFieldId, contactFieldValue, completionListener = completionListener) if (shouldRestartSession) { + doSetContact(contactFieldId, contactFieldValue, completionListener = completionListener) if (!sessionIdHolder.sessionId.isNullOrEmpty()) { session.endSession { if (it != null) { @@ -85,18 +85,26 @@ class DefaultMobileEngageInternal( } override fun clearContact(completionListener: CompletionListener?) { - if (!sessionIdHolder.sessionId.isNullOrEmpty()) { - session.endSession { - if (it != null) { - Logger.error(CrashLog(it)) + if (hasContactToken() && !requestContext.hasContactIdentification()) { + completionListener?.onCompleted(null) + } else { + if (!sessionIdHolder.sessionId.isNullOrEmpty()) { + session.endSession { + if (it != null) { + Logger.error(CrashLog(it)) + } + doClearContact(completionListener) } + } else { doClearContact(completionListener) } - } else { - doClearContact(completionListener) } } + private fun hasContactToken(): Boolean { + return !requestContext.contactTokenStorage.get().isNullOrEmpty() + } + internal fun doClearContact(completionListener: CompletionListener?) { resetContext()