diff --git a/core/src/main/java/com/emarsys/core/util/AndroidVersionUtils.kt b/core/src/main/java/com/emarsys/core/util/AndroidVersionUtils.kt
index d1f71d58..a7d75cb9 100644
--- a/core/src/main/java/com/emarsys/core/util/AndroidVersionUtils.kt
+++ b/core/src/main/java/com/emarsys/core/util/AndroidVersionUtils.kt
@@ -22,7 +22,11 @@ object AndroidVersionUtils {
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- val isUOrAbove: Boolean
+ val isSOrAbove: Boolean
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
+ get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
+
+ val isUOrAbove: Boolean
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
diff --git a/emarsys-sdk/src/main/AndroidManifest.xml b/emarsys-sdk/src/main/AndroidManifest.xml
index 9c9b0919..07efa8de 100644
--- a/emarsys-sdk/src/main/AndroidManifest.xml
+++ b/emarsys-sdk/src/main/AndroidManifest.xml
@@ -9,6 +9,14 @@
+
+
+
+
+
private lateinit var mockInitialEnterTriggerEnabledStorage: Storage
private lateinit var mockPendingIntentProvider: GeofencePendingIntentProvider
@@ -110,91 +114,63 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
@Before
fun setUp() {
concurrentHandlerHolder = ConcurrentHandlerHolderFactory.create()
- mockInitialEnterTriggerEnabledStorage = mock()
-
- whenever(mockInitialEnterTriggerEnabledStorage.get()).thenReturn(false)
-
- mockResponseModel = mock()
- mockGeofenceRequestModel = mock()
- mockRequestModelFactory = mock()
- whenever(mockRequestModelFactory.createFetchGeofenceRequest()).thenReturn(
- mockGeofenceRequestModel
- )
-
- mockContext = mock()
- val context: Context = getInstrumentation().targetContext
- whenever(mockContext.packageName).thenReturn(
- "com.emarsys.mobileengage.test"
- )
- if (!AndroidVersionUtils.isBelow30) {
- whenever(mockContext.attributionTag).thenReturn("tag")
- }
+ mockInitialEnterTriggerEnabledStorage = mockk(relaxed = true)
- val intent = Intent("com.emarsys.sdk.GEOFENCE_ACTION")
- pendingIntent =
- PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ every { mockInitialEnterTriggerEnabledStorage.get() } returns false
- mockPendingIntentProvider = mock()
- whenever(mockPendingIntentProvider.providePendingIntent()).thenReturn(pendingIntent)
+ mockResponseModel = mockk(relaxed = true)
+ mockGeofenceRequestModel = mockk(relaxed = true)
+ mockRequestModelFactory = mockk(relaxed = true)
+ every { mockRequestModelFactory.createFetchGeofenceRequest() } returns mockGeofenceRequestModel
+ mockPendingIntentProvider = mockk(relaxed = true)
+ pendingIntent = mockk(relaxed = true)
+ every { mockPendingIntentProvider.providePendingIntent() } returns pendingIntent
fakeRequestManager =
- spy(FakeRequestManager(FakeRequestManager.ResponseType.SUCCESS, mockResponseModel))
+ spyk(FakeRequestManager(FakeRequestManager.ResponseType.SUCCESS, mockResponseModel))
- mockGeofenceResponseMapper = mock()
- whenever(
- mockGeofenceResponseMapper.map(any())
- ).thenReturn(GeofenceResponse(listOf()))
-
- mockPermissionChecker = mock()
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
+ mockGeofenceResponseMapper = mockk(relaxed = true)
+ every { mockGeofenceResponseMapper.map(any()) } returns GeofenceResponse(listOf())
+ mockPermissionChecker = mockk(relaxed = true)
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) } returns PackageManager.PERMISSION_DENIED
- mockLocation = mock()
+ mockLocation = mockk(relaxed = true)
- mockTask = mock()
- argumentCaptor> {
- whenever(mockTask.addOnCompleteListener(capture())).thenAnswer {
- firstValue.onComplete(mockTask)
- mockTask
- }
+ mockTask = mockk(relaxed = true)
+ every { mockTask.addOnCompleteListener(any()) } answers {
+ firstArg>().onComplete(mockTask)
+ mockTask
}
-
- mockFusedLocationProviderClient = mock()
- whenever(
+ mockFusedLocationProviderClient = mockk(relaxed = true)
+ every {
mockFusedLocationProviderClient.requestLocationUpdates(
any(),
any()
)
- ).thenReturn(mockTask)
+ } returns mockTask
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(mockLocation)
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(mockLocation)
- )
- mockGeofenceFilter = mock()
-
- whenever(
+ mockGeofenceFilter = mockk(relaxed = true)
+ every {
mockGeofenceFilter.findNearestGeofences(
any(),
any()
)
- ).thenReturn(nearestGeofencesWithRefreshArea)
-
- mockLocation = mock()
- whenever(mockLocation.latitude).thenReturn(47.493165)
- whenever(mockLocation.longitude).thenReturn(19.058359)
-
- mockGeofencingClient = mock()
+ } returns nearestGeofencesWithRefreshArea
- mockActionCommandFactory = mock()
- mockCacheableEventHandler = mock()
- mockEnabledStorage = mock()
+ mockLocation = mockk(relaxed = true)
+ every { mockLocation.latitude } returns 47.493165
+ every { mockLocation.longitude } returns 19.058359
- whenever(mockEnabledStorage.get()).thenReturn(true)
+ mockGeofencingClient = mockk(relaxed = true)
+ mockActionCommandFactory = mockk(relaxed = true)
+ mockCacheableEventHandler = mockk(relaxed = true)
+ mockEnabledStorage = mockk(relaxed = true)
+ every { mockEnabledStorage.get() } returns true
geofenceInternal = DefaultGeofenceInternal(
mockRequestModelFactory,
@@ -204,24 +180,6 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
mockFusedLocationProviderClient,
mockGeofenceFilter,
mockGeofencingClient,
- mockContext,
- mockActionCommandFactory,
- mockCacheableEventHandler,
- mockEnabledStorage,
- mockPendingIntentProvider,
- concurrentHandlerHolder,
- mockInitialEnterTriggerEnabledStorage
- )
-
- geofenceInternalWithMockContext = DefaultGeofenceInternal(
- mockRequestModelFactory,
- fakeRequestManager,
- mockGeofenceResponseMapper,
- mockPermissionChecker,
- mockFusedLocationProviderClient,
- mockGeofenceFilter,
- mockGeofencingClient,
- mockContext,
mockActionCommandFactory,
mockCacheableEventHandler,
mockEnabledStorage,
@@ -235,22 +193,22 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
fun testFetchGeofences_shouldSendRequest_viaRequestManager_submitNow() {
geofenceInternal.fetchGeofences(null)
- verify(fakeRequestManager).submitNow(any(), any())
+ verify { fakeRequestManager.submitNow(any(), any()) }
}
@Test
fun testFetchGeofences_callsMapOnResponseMapper_onSuccess() {
geofenceInternal.fetchGeofences(null)
- verify(mockGeofenceResponseMapper).map(mockResponseModel)
+ verify { mockGeofenceResponseMapper.map(mockResponseModel) }
}
@Test
fun testFetchGeofences_callsEnable_onSuccess() {
- val spyGeofenceInternal = spy(geofenceInternal)
+ val spyGeofenceInternal = spyk(geofenceInternal)
spyGeofenceInternal.fetchGeofences(null)
- verify(spyGeofenceInternal).enable(null)
+ verify { spyGeofenceInternal.enable(null) }
}
@Test
@@ -258,145 +216,39 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
geofenceInternal.enable(null)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
- }
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- }
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
- fun testEnable_registersGeofenceBroadcastReceiverAboveTiramisu() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.enable(null)
-
- verify(
- mockContext,
- timeout(100).times(1)
- ).registerReceiver(
- any(),
- any(),
- eq(Context.RECEIVER_EXPORTED)
- )
-
- }
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 32)
- fun testEnable_registersGeofenceBroadcastReceiverBelowTiramisu() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.enable(null)
-
- verify(
- mockContext,
- timeout(100).times(1)
- ).registerReceiver(any(), any())
- }
- }
-
- @Test
- fun testDisable_unregistersGeofenceBroadcastReceiver() {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.disable()
-
- verify(mockContext).unregisterReceiver(any())
- verify(mockFusedLocationProviderClient).removeLocationUpdates(pendingIntent)
- }
-
- @Test
- fun testDisable_shouldNotCallUnregisterReceiver_ifReceiversAreNotRegistered() {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.disable()
- geofenceInternalWithMockContext.disable()
-
- verify(
- mockContext,
- timeout(100).times(1)
- ).unregisterReceiver(any())
-
- }
-
- @Test
- fun testDisable_shouldNotCrash_whenUnregisterReceiverCalled_multipleTimes() {
- geofenceInternal.enable(null)
- geofenceInternal.disable()
- ReflectionTestUtils.setInstanceField(
- geofenceInternalWithMockContext,
- "receiverRegistered",
- true
- )
- geofenceInternal.disable()
- }
-
- @Test
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
- fun testDisableAboveTiramisu() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.disable()
- geofenceInternalWithMockContext.enable(null)
-
- verify(
- mockContext, timeout(100).times(2)
- ).registerReceiver(
- any(),
- any(),
- eq(Context.RECEIVER_EXPORTED)
- )
- }
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.S_V2)
- fun testDisableBelowTiramisu() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.disable()
- geofenceInternalWithMockContext.enable(null)
-
- verify(
- mockContext, timeout(100).times(2)
- ).registerReceiver(any(), any())
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
}
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
}
@Test
fun testFetch_doNotFetch_whenGeofenceIsNotEnabled() {
- whenever(mockEnabledStorage.get()).thenReturn(false)
+ every { mockEnabledStorage.get() } returns false
geofenceInternal.fetchGeofences(null)
- verify(mockRequestModelFactory, times(0)).createFetchGeofenceRequest()
- verify(fakeRequestManager, times(0)).submit(any(), any())
+ verify(exactly = 0) { mockRequestModelFactory.createFetchGeofenceRequest() }
+ verify(exactly = 0) { fakeRequestManager.submit(any(), any()) }
}
@Test
fun testFetch_fetch_whenGeofenceIsEnabled() {
- whenever(mockEnabledStorage.get()).thenReturn(
- true
- )
+ every { mockEnabledStorage.get() } returns true
geofenceInternal.fetchGeofences(null)
- verify(mockRequestModelFactory).createFetchGeofenceRequest()
- verify(fakeRequestManager).submitNow(eq(mockGeofenceRequestModel), any())
-
+ verify { mockRequestModelFactory.createFetchGeofenceRequest() }
+ verify { fakeRequestManager.submitNow(eq(mockGeofenceRequestModel), any()) }
}
@Test
fun testEnable_shouldSetEnabledStorage_andFetchIfWasDisabled() {
val latch = CountDownLatch(1)
var completionListenerHasBeenCalled = false
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
- whenever(mockEnabledStorage.get()).thenReturn(false).thenReturn(true)
+ every { mockEnabledStorage.get() } returns false andThen true
geofenceInternal.enable {
it shouldBe null
@@ -406,91 +258,65 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
latch.await()
completionListenerHasBeenCalled shouldBe true
- verify(mockEnabledStorage, timeout(100)).set(true)
- verify(mockRequestModelFactory).createFetchGeofenceRequest()
+ verify { mockEnabledStorage.set(true) }
+ verify { mockRequestModelFactory.createFetchGeofenceRequest() }
}
-
@Test
fun testDisable_shouldClearEnabledStorage() {
- geofenceInternalWithMockContext.enable(null)
- geofenceInternalWithMockContext.disable()
-
- verify(mockEnabledStorage).set(false)
+ geofenceInternal.enable(null)
+ geofenceInternal.disable()
+ verify { mockEnabledStorage.set(false) }
}
@Test
fun testIsEnabled_shouldReturnTrue_whenGeofenceIsEnabled() {
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockEnabledStorage.get()).thenReturn(false).thenReturn(false).thenReturn(true)
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockEnabledStorage.get() } returns false andThen false andThen true
geofenceInternal.isEnabled() shouldBe false
geofenceInternal.enable(null)
- verify(mockEnabledStorage, timeout(100)).set(true)
+ verify { mockEnabledStorage.set(true) }
geofenceInternal.isEnabled() shouldBe true
}
@Test
fun testIsEnabled_shouldReturnFalse_whenGeofenceIsDisabled() {
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockEnabledStorage.get()).thenReturn(true).thenReturn(true).thenReturn(false)
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockEnabledStorage.get() } returns true andThen true andThen false
geofenceInternal.isEnabled() shouldBe true
geofenceInternal.disable()
- verify(mockEnabledStorage, timeout(100)).set(false)
+ verify { mockEnabledStorage.set(false) }
geofenceInternal.isEnabled() shouldBe false
}
@Test
fun testEnable_fetchLastKnownLocation_fromLocationManager_whenPermissionGranted() {
- whenever(
- mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- ).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
geofenceInternal.enable(null)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
- verify(mockFusedLocationProviderClient).lastLocation
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
+ verify { mockFusedLocationProviderClient.lastLocation }
}
}
@Test
fun testEnable_fetchLastKnownLocation_fromLocationManager_whenPermissionGranted_withCompletionListener() {
val latch = CountDownLatch(1)
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
var completionListenerHasBeenCalled = false
@@ -502,28 +328,19 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
latch.await()
completionListenerHasBeenCalled shouldBe true
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
}
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
-
- verify(mockFusedLocationProviderClient).lastLocation
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
+ verify { mockFusedLocationProviderClient.lastLocation }
}
@Test
fun testEnable_returnDoNoTReturn_PermissionForLocationException_whenFineLocationPermissionDenied_andCoarseLocationGranted() {
val latch = CountDownLatch(1)
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_DENIED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
var completionListenerHasBeenCalled = false
@@ -535,36 +352,22 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
latch.await()
completionListenerHasBeenCalled shouldBe true
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
}
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
-
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
- verify(mockFusedLocationProviderClient).lastLocation
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) }
+ verify { mockFusedLocationProviderClient.lastLocation }
}
@Test
fun testEnable_returnNoPermissionForLocationException_whenFineLocationPermissionDenied() {
val geofenceResponse = GeofenceResponse(listOf(), 0.0)
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(
- mockLocation
- )
- )
-
- whenever(mockGeofenceResponseMapper.map(any())).thenReturn(
- geofenceResponse
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_DENIED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(mockLocation)
+ every { mockGeofenceResponseMapper.map(any()) } returns geofenceResponse
var completionListenerHasBeenCalled = false
geofenceInternal.enable {
@@ -575,40 +378,23 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
completionListenerHasBeenCalled shouldBe true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
}
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
-
- verifyNoInteractions(
- mockFusedLocationProviderClient
- )
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
- verifyNoInteractions(mockGeofenceFilter)
+ confirmVerified(mockFusedLocationProviderClient)
+ confirmVerified(mockGeofenceFilter)
}
-
@Test
fun testEnable_returnNoPermissionForLocationException_whenBackgroundLocationPermissionDenied() {
val geofenceResponse = GeofenceResponse(listOf(), 0.0)
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
-
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(
- mockLocation
- )
- )
-
- whenever(mockGeofenceResponseMapper.map(any())).thenReturn(
- geofenceResponse
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_DENIED
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(mockLocation)
+ every { mockGeofenceResponseMapper.map(any()) } returns geofenceResponse
var completionListenerHasBeenCalled = false
geofenceInternal.enable {
@@ -621,35 +407,22 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
completionListenerHasBeenCalled shouldBe true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
- verifyNoInteractions(mockFusedLocationProviderClient)
- verifyNoInteractions(mockGeofenceFilter)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
+ confirmVerified(mockFusedLocationProviderClient)
+ confirmVerified(mockGeofenceFilter)
}
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
}
@Test
fun testEnable_returnNoPermissionForLocationException_whenPermissionsDenied() {
val geofenceResponse = GeofenceResponse(listOf(), 0.0)
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_DENIED
- )
-
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(
- mockLocation
- )
- )
-
- whenever(mockGeofenceResponseMapper.map(any())).thenReturn(
- geofenceResponse
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_DENIED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_DENIED
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(mockLocation)
+ every { mockGeofenceResponseMapper.map(any()) } returns geofenceResponse
var completionListenerHasBeenCalled = false
geofenceInternal.enable {
@@ -664,92 +437,73 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
completionListenerHasBeenCalled shouldBe true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) }
}
- verify(mockPermissionChecker).checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
-
- verifyNoInteractions(
- mockFusedLocationProviderClient
- )
-
- verifyNoInteractions(mockGeofenceFilter)
+ verify { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) }
+ confirmVerified(mockFusedLocationProviderClient)
+ confirmVerified(mockGeofenceFilter)
}
@Test
fun testEnable_registersGeofencesWithAdditionalRefreshArea() {
val geofenceResponse = GeofenceResponse(listOf(), 0.3)
- whenever(mockGeofenceResponseMapper.map(any())).thenReturn(
- geofenceResponse
- )
+ every { mockGeofenceResponseMapper.map(any()) } returns geofenceResponse
- val spyGeofenceInternal: GeofenceInternal = spy(geofenceInternal)
+ val spyGeofenceInternal: GeofenceInternal = spyk(geofenceInternal)
spyGeofenceInternal.fetchGeofences(null)
- whenever(mockGeofenceFilter.findNearestGeofences(any(), any())).thenReturn(
- nearestGeofencesWithoutRefreshArea
- )
+ every {
+ mockGeofenceFilter.findNearestGeofences(
+ any(),
+ any()
+ )
+ } returns nearestGeofencesWithoutRefreshArea
val currentLocation = Location(LocationManager.GPS_PROVIDER).apply {
this.latitude = 47.493160
this.longitude = 19.058355
}
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(
- currentLocation
- )
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(
+ currentLocation
)
spyGeofenceInternal.enable(null)
- argumentCaptor> {
- verify(spyGeofenceInternal, times(2)).registerGeofences(capture())
+ val slot = mutableListOf>()
+ verify(exactly = 2) { spyGeofenceInternal.registerGeofences(capture(slot)) }
+ slot.size shouldBe 2
- lastValue.size shouldBe 4
- lastValue[3].toString() shouldBe refreshArea.toString()
- }
+ slot[1].size shouldBe 4
+ slot[1][3].toString() shouldBe refreshArea.toString()
}
@Test
fun testEnable_shouldNotCrash_registersGeofencesWhenRefreshRadiusWouldBeNegative() {
val geofenceResponse = GeofenceResponse(listOf(), 1.0)
- whenever(mockGeofenceResponseMapper.map(any())).thenReturn(
- geofenceResponse
- )
+ every { mockGeofenceResponseMapper.map(any()) } returns geofenceResponse
- val spyGeofenceInternal: GeofenceInternal = spy(geofenceInternal)
+ val spyGeofenceInternal: GeofenceInternal = spyk(geofenceInternal)
spyGeofenceInternal.fetchGeofences(null)
- whenever(mockGeofenceFilter.findNearestGeofences(any(), any())).thenReturn(
- listOf(nearestGeofencesWithoutRefreshArea[0])
+ every { mockGeofenceFilter.findNearestGeofences(any(), any()) } returns listOf(
+ nearestGeofencesWithoutRefreshArea[0]
)
- val currentLocation = (Location(LocationManager.GPS_PROVIDER).apply {
+ val currentLocation = Location(LocationManager.GPS_PROVIDER).apply {
this.latitude = 47.493160
this.longitude = 19.058355
- })
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
-
- whenever(mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)).thenReturn(
- PackageManager.PERMISSION_GRANTED
- )
+ }
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(currentLocation)
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockPermissionChecker.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) } returns PackageManager.PERMISSION_GRANTED
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(
+ currentLocation
)
spyGeofenceInternal.enable(null)
@@ -762,73 +516,44 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
refreshArea
)
val geofencingRequest =
- GeofencingRequest.Builder().addGeofences(geofencesToTest).setInitialTrigger(0)
- .build()
- argumentCaptor {
- whenever(
- mockGeofencingClient.addGeofences(
- capture(),
- any()
- )
- ).thenReturn(
- mockTask
- )
+ GeofencingRequest.Builder().addGeofences(geofencesToTest).setInitialTrigger(0).build()
- geofenceInternal.registerGeofences(nearestGeofencesWithoutRefreshArea + refreshArea)
+ val slot = slot()
+ every { mockGeofencingClient.addGeofences(capture(slot), any()) } returns mockTask
- firstValue.initialTrigger shouldBe geofencingRequest.initialTrigger
- firstValue.geofences.forEachIndexed { index, geofence ->
- geofence.requestId shouldBe geofencingRequest.geofences[index].requestId
- }
- }
+ geofenceInternal.registerGeofences(nearestGeofencesWithoutRefreshArea + refreshArea)
+ slot.captured.initialTrigger shouldBe geofencingRequest.initialTrigger
+ slot.captured.geofences.forEachIndexed { index, geofence ->
+ geofence.requestId shouldBe geofencingRequest.geofences[index].requestId
+ }
}
@Test
fun testRegisterGeofences_geofencingRequest_shouldIncludeInitialEnterTrigger() {
val geofencesToTest =
nearestGeofencesWithoutRefreshArea.map { createGeofence(it) } + createGeofence(
- Companion.refreshArea
- )
- val geofencingRequest =
- GeofencingRequest.Builder().addGeofences(geofencesToTest).build()
- argumentCaptor {
- whenever(
- mockGeofencingClient.addGeofences(
- capture(),
- any()
- )
- ).thenReturn(
- mockTask
+ refreshArea
)
+ val geofencingRequest = GeofencingRequest.Builder().addGeofences(geofencesToTest).build()
+ val slot = slot()
+ every { mockGeofencingClient.addGeofences(capture(slot), any()) } returns mockTask
- geofenceInternal.setInitialEnterTriggerEnabled(true)
- geofenceInternal.registerGeofences(nearestGeofencesWithoutRefreshArea + refreshArea)
+ geofenceInternal.setInitialEnterTriggerEnabled(true)
+ geofenceInternal.registerGeofences(nearestGeofencesWithoutRefreshArea + refreshArea)
- verify(mockInitialEnterTriggerEnabledStorage).set(true)
- firstValue.initialTrigger shouldBe GeofencingRequest.INITIAL_TRIGGER_ENTER
- firstValue.geofences.forEachIndexed { index, geofence ->
- geofence.requestId shouldBe geofencingRequest.geofences[index].requestId
- }
+ verify { mockInitialEnterTriggerEnabledStorage.set(true) }
+ slot.captured.initialTrigger shouldBe GeofencingRequest.INITIAL_TRIGGER_ENTER
+ slot.captured.geofences.forEachIndexed { index, geofence ->
+ geofence.requestId shouldBe geofencingRequest.geofences[index].requestId
}
}
@Test
- fun testOnGeofenceTriggered() {
+ fun testOnGeofenceTriggered_shouldExecuteActions_ifFeatureIsEnabled_andNearestGeofences_isNotEmpty() {
val latch = CountDownLatch(1)
- val mockAction: Runnable = mock()
- val appEventAction = JSONObject(
- """
- {
- "type": "MEAppEvent",
- "name": "nameValue",
- "payload": {
- "someKey": "someValue"
- }
- }
- """.trimIndent()
- )
+ val mockAction: Runnable = mockk(relaxed = true)
val testTrigger =
Trigger(id = "appEventActionId", type = TriggerType.ENTER, action = appEventAction)
val testExitTrigger =
@@ -849,10 +574,11 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
MEGeofence("geofenceId5", 47.492292, 19.056440, 10.0, null, listOf(trigger))
)
ReflectionTestUtils.setInstanceField(geofenceInternal, "nearestGeofences", allGeofences)
- whenever(mockAction.run()).thenAnswer { latch.countDown() }
- whenever(mockActionCommandFactory.createActionCommand(appEventAction)).thenReturn(
- mockAction
- )
+ every { mockAction.run() } answers { latch.countDown() }
+ every { mockActionCommandFactory.createActionCommand(appEventAction) } returns mockAction
+
+ every { mockEnabledStorage.get() } returns true
+
geofenceInternal.onGeofenceTriggered(
listOf(
TriggeringEmarsysGeofence(
@@ -862,41 +588,75 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
)
)
- verify(
- mockActionCommandFactory, times(1)
- ).createActionCommand(
- appEventAction
- )
+ verify { mockActionCommandFactory.createActionCommand(appEventAction) }
latch.await()
- verify(mockAction).run()
+ verify { mockAction.run() }
}
+ @Test
+ fun testOnGeofenceTriggered_shouldFetchFirst_andHandleActions_ifFeatureIsEnabled_andNearestGeofences_isEmpty() {
+ every { mockEnabledStorage.get() } returns true
+ ReflectionTestUtils.setInstanceField(
+ geofenceInternal,
+ "nearestGeofences",
+ emptyList()
+ )
+ every { mockGeofenceResponseMapper.map(mockResponseModel) } returns GeofenceResponse(
+ listOf(
+ GeofenceGroup(
+ "group1", null, listOf(
+ com.emarsys.mobileengage.api.geofence.Geofence(
+ "geofenceId1", 47.493827, 19.060715, 10.0, null, listOf(
+ Trigger("triggerId", TriggerType.ENTER, 0, appEventAction)
+ )
+ )
+ )
+ )
+ )
+ )
+ geofenceInternal.onGeofenceTriggered(
+ listOf(
+ TriggeringEmarsysGeofence(
+ "geofenceId1",
+ TriggerType.ENTER
+ )
+ )
+ )
+
+ verify { mockGeofenceResponseMapper.map(mockResponseModel) }
+ verify { mockActionCommandFactory.createActionCommand(any()) }
+ }
@Test
- fun testOnGeofenceTriggered_onRefreshArea_recalculateGeofences() {
- val spyGeofenceInternal = spy(geofenceInternal)
- val latch = CountDownLatch(1)
- val mockAction: Runnable = mock()
- val appEventAction = JSONObject(
- """
- {
- "type": "MEAppEvent",
- "name": "nameValue",
- "payload": {
- "someKey": "someValue"
- }
- }
- """.trimIndent()
+ fun testOnGeofenceTriggered_shouldNotHandleActions_ifFeatureIsDisabled() {
+ every { mockEnabledStorage.get() } returns false
+
+ geofenceInternal.onGeofenceTriggered(
+ listOf(
+ TriggeringEmarsysGeofence(
+ "testId",
+ TriggerType.ENTER
+ )
+ )
)
+ verify(exactly = 0) { mockActionCommandFactory.createActionCommand(any()) }
+ }
+
+ @Test
+ fun testOnGeofenceTriggered_onRefreshArea_recalculateGeofences() {
+ val spyGeofenceInternal = spyk(geofenceInternal)
+ val latch = CountDownLatch(1)
+ val mockAction: Runnable = mockk(relaxed = true)
val testTrigger =
Trigger(id = "appEventActionId", type = TriggerType.ENTER, action = appEventAction)
val trigger = Trigger(id = "triggerId", type = TriggerType.ENTER, action = JSONObject())
- val currentLocation = (Location(LocationManager.GPS_PROVIDER).apply {
+ val currentLocation = Location(LocationManager.GPS_PROVIDER).apply {
this.latitude = 47.493160
this.longitude = 19.058355
- })
+ }
+ every { mockEnabledStorage.get() } returns true
val allGeofences = listOf(
MEGeofence("geofenceId1", 47.493160, 19.058355, 10.0, null, listOf(trigger)),
MEGeofence("geofenceId2", 47.493812, 19.058537, 10.0, null, listOf(trigger)),
@@ -952,51 +712,39 @@ class DefaultGeofenceInternalTest : AnnotationSpec() {
"geofenceResponse",
geofenceResponse
)
- whenever(mockAction.run()).thenAnswer { latch.countDown() }
- whenever(mockActionCommandFactory.createActionCommand(appEventAction)).thenReturn(
- mockAction
- )
+ every { mockAction.run() } answers { latch.countDown() }
+ every { mockActionCommandFactory.createActionCommand(appEventAction) } returns mockAction
- whenever(mockFusedLocationProviderClient.lastLocation).thenReturn(
- FakeLocationTask(currentLocation)
+ every { mockFusedLocationProviderClient.lastLocation } returns FakeLocationTask(
+ currentLocation
)
- whenever(
+ every {
mockGeofenceFilter.findNearestGeofences(
currentLocation,
geofenceResponse
)
- ).thenReturn(
- nearestGeofences2
- )
+ } returns nearestGeofences2
+ val slot = slot>()
spyGeofenceInternal.onGeofenceTriggered(
listOf(
- TriggeringEmarsysGeofence(
- "geofenceId1",
- TriggerType.ENTER
- ), TriggeringEmarsysGeofence("refreshArea", TriggerType.EXIT)
+ TriggeringEmarsysGeofence("geofenceId1", TriggerType.ENTER),
+ TriggeringEmarsysGeofence("refreshArea", TriggerType.EXIT)
)
)
- argumentCaptor> {
- verify(spyGeofenceInternal).registerGeofences(capture())
- verify(mockActionCommandFactory).createActionCommand(appEventAction)
+ verify { spyGeofenceInternal.registerGeofences(capture(slot)) }
+ verify { mockActionCommandFactory.createActionCommand(appEventAction) }
- latch.await()
+ latch.await()
- verify(mockAction).run()
+ verify { mockAction.run() }
- verify(
- mockGeofenceFilter
- ).findNearestGeofences(
- currentLocation,
- geofenceResponse
- )
+ verify { mockGeofenceFilter.findNearestGeofences(currentLocation, geofenceResponse) }
- firstValue.size shouldBe 2
- firstValue[1].toString() shouldBe refreshArea.toString()
- }
+ slot.captured.size shouldBe 2
+ slot.captured[1].toString() shouldBe refreshArea.toString()
}
private fun createGeofence(geofence: MEGeofence): Geofence {
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 c826c99c..cc57ce13 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
@@ -2,8 +2,6 @@ package com.emarsys.mobileengage.geofence
import android.Manifest
import android.app.PendingIntent
-import android.content.Context
-import android.content.IntentFilter
import android.content.pm.PackageManager
import android.location.Location
import androidx.annotation.RequiresPermission
@@ -43,7 +41,6 @@ class DefaultGeofenceInternal(
private val fusedLocationProviderClient: FusedLocationProviderClient,
private val geofenceFilter: GeofenceFilter,
private val geofencingClient: GeofencingClient,
- private val context: Context,
private val actionCommandFactory: ActionCommandFactory,
private val geofenceCacheableEventHandler: CacheableEventHandler,
private val geofenceEnabledStorage: Storage,
@@ -57,7 +54,6 @@ class DefaultGeofenceInternal(
const val MAX_WAIT_TIME: Long = 60_000
}
- private val geofenceBroadcastReceiver = GeofenceBroadcastReceiver(concurrentHandlerHolder)
private var geofenceResponse: GeofenceResponse? = null
private var nearestGeofences: MutableList = mutableListOf()
override val registeredGeofences: List
@@ -68,7 +64,6 @@ class DefaultGeofenceInternal(
private val geofencePendingIntent: PendingIntent by lazy {
geofencePendingIntentProvider.providePendingIntent()
}
- private var receiverRegistered = false
private var initialEnterTriggerEnabled = initialEnterTriggerEnabledStorage.get() ?: false
private var initialDwellingTriggerEnabled = false
private var initialExitTriggerEnabled = false
@@ -115,7 +110,6 @@ class DefaultGeofenceInternal(
}
}
registerNearestGeofences(completionListener)
- registerBroadcastReceiver()
} else {
completionListener?.onCompleted(MissingPermissionException("Couldn't acquire permission for $missingPermissions"))
}
@@ -123,13 +117,9 @@ class DefaultGeofenceInternal(
override fun disable() {
if (geofenceEnabledStorage.get()) {
- if (receiverRegistered) {
- try {
- context.unregisterReceiver(geofenceBroadcastReceiver)
- receiverRegistered = false
- fusedLocationProviderClient.removeLocationUpdates(geofencePendingIntent)
- } catch (ignored: IllegalArgumentException) {
- }
+ try {
+ fusedLocationProviderClient.removeLocationUpdates(geofencePendingIntent)
+ } catch (ignored: IllegalArgumentException) {
}
geofenceEnabledStorage.set(false)
@@ -146,26 +136,6 @@ class DefaultGeofenceInternal(
return geofenceEnabledStorage.get()
}
- private fun registerBroadcastReceiver() {
- if (!receiverRegistered) {
- concurrentHandlerHolder.postOnMain {
- if (AndroidVersionUtils.isTiramisuOrAbove) {
- context.registerReceiver(
- geofenceBroadcastReceiver,
- IntentFilter("com.emarsys.sdk.GEOFENCE_ACTION"),
- Context.RECEIVER_EXPORTED
- )
- } else {
- context.registerReceiver(
- geofenceBroadcastReceiver,
- IntentFilter("com.emarsys.sdk.GEOFENCE_ACTION"),
- )
- }
- }
- receiverRegistered = true
- }
- }
-
@RequiresPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
private fun validateBackgroundPermission() {
}
@@ -299,6 +269,18 @@ class DefaultGeofenceInternal(
}
override fun onGeofenceTriggered(triggeringEmarsysGeofences: List) {
+ if (isEnabled()) {
+ if (nearestGeofences.isEmpty()) {
+ fetchGeofences {
+ handleActions(triggeringEmarsysGeofences)
+ }
+ } else {
+ handleActions(triggeringEmarsysGeofences)
+ }
+ }
+ }
+
+ private fun handleActions(triggeringEmarsysGeofences: List) {
extractActions(triggeringEmarsysGeofences).run {
executeActions(this)
}
diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofenceBroadcastReceiver.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofenceBroadcastReceiver.kt
index 2dcfafaa..42d34d46 100644
--- a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofenceBroadcastReceiver.kt
+++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofenceBroadcastReceiver.kt
@@ -15,6 +15,7 @@ import com.google.android.gms.location.GeofencingEvent
class GeofenceBroadcastReceiver(val concurrentHandlerHolder: ConcurrentHandlerHolder) :
BroadcastReceiver() {
+ constructor() : this(mobileEngage().concurrentHandlerHolder)
override fun onReceive(context: Context, intent: Intent) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
@@ -52,10 +53,17 @@ class GeofenceBroadcastReceiver(val concurrentHandlerHolder: ConcurrentHandlerHo
private fun logTriggeringGeofences(triggeringEmarsysGeofences: List) {
triggeringEmarsysGeofences.forEach {
val status = mapOf(
- "triggerType" to it.triggerType,
- "geofenceId" to it.geofenceId
+ "triggerType" to it.triggerType,
+ "geofenceId" to it.geofenceId
+ )
+ Logger.debug(
+ StatusLog(
+ GeofenceBroadcastReceiver::class.java,
+ SystemUtils.getCallerMethodName(),
+ mapOf(),
+ status
+ )
)
- Logger.debug(StatusLog(GeofenceBroadcastReceiver::class.java, SystemUtils.getCallerMethodName(), mapOf(), status))
}
}
diff --git a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofencePendingIntentProvider.kt b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofencePendingIntentProvider.kt
index b0fa1405..112d2347 100644
--- a/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofencePendingIntentProvider.kt
+++ b/mobile-engage/src/main/java/com/emarsys/mobileengage/geofence/GeofencePendingIntentProvider.kt
@@ -11,15 +11,15 @@ class GeofencePendingIntentProvider(private val context: Context) {
fun providePendingIntent(): PendingIntent {
val intent = Intent("com.emarsys.sdk.GEOFENCE_ACTION")
intent.setPackage(context.packageName)
- if (AndroidVersionUtils.isUOrAbove) {
- return PendingIntent.getBroadcast(
+ return if (AndroidVersionUtils.isSOrAbove) {
+ PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
} else {
- return PendingIntent.getBroadcast(
+ PendingIntent.getBroadcast(
context,
0,
intent,
diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts
index 620f0f02..6750e8fc 100644
--- a/sample/build.gradle.kts
+++ b/sample/build.gradle.kts
@@ -159,7 +159,6 @@ dependencies {
implementation(libs.google.firebase.common)
implementation(libs.google.firebase.messaging)
implementation(libs.play.services.auth)
- implementation(libs.google.accompanist.swipetorefresh)
implementation(libs.google.gson)
debugImplementation(libs.androidx.compose.ui.tooling)
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index a8cb8ddc..f993d2d9 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -26,7 +26,8 @@
+ android:exported="true"
+ android:windowSoftInputMode="adjustResize">
diff --git a/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt b/sample/src/main/kotlin/com/emarsys/sample/SampleApplication.kt
index 9b9694c6..48a6078b 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 = Prefs.applicationCode.ifEmpty { null },
merchantId = Prefs.merchantId,
verboseConsoleLoggingEnabled = true
)
@@ -37,7 +37,10 @@ class SampleApplication : Application(), EventHandler, NotificationInformationLi
Emarsys.setup(config)
createNotificationChannels()
setupEventHandlers()
+ setContactIfPresent(context)
+ }
+ private fun setContactIfPresent(context: Context) {
if (Prefs.contactFieldId != 0 && Prefs.contactFieldValue.isNotEmpty()) {
Emarsys.setContact(
contactFieldValue = Prefs.contactFieldValue,
@@ -51,7 +54,7 @@ class SampleApplication : Application(), EventHandler, NotificationInformationLi
}
}
- fun setupEventHandlers() {
+ private fun setupEventHandlers() {
if (Prefs.applicationCode.isNotEmpty()) {
Emarsys.push.setNotificationEventHandler(this)
Emarsys.push.setSilentMessageEventHandler(this)
diff --git a/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardScreen.kt
index b6202c1b..560cc1db 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardScreen.kt
@@ -1,6 +1,5 @@
package com.emarsys.sample.dashboard
-import android.app.Application
import android.content.Context
import android.os.Build
import android.util.Log
@@ -22,12 +21,10 @@ import androidx.compose.ui.res.stringResource
import coil.annotation.ExperimentalCoilApi
import com.emarsys.Emarsys
import com.emarsys.sample.R
-import com.emarsys.sample.SampleApplication
import com.emarsys.sample.dashboard.button.CheckLocationPermissionsButton
import com.emarsys.sample.dashboard.button.CopyPushTokenButton
import com.emarsys.sample.dashboard.button.TrackPushTokenButton
import com.emarsys.sample.dashboard.button.trackPushToken
-import com.emarsys.sample.main.sdkinfo.SetupInfo
import com.emarsys.sample.ui.component.ColumnWithTapGesture
import com.emarsys.sample.ui.component.button.GoogleSignInButton
import com.emarsys.sample.ui.component.button.StyledTextButton
@@ -46,9 +43,8 @@ import kotlin.system.exitProcess
class DashboardScreen(
override val context: Context,
- override val application: Application
+ private val viewModel: DashboardViewModel
) : DetailScreen() {
- private val viewModel = DashboardViewModel()
@OptIn(ExperimentalMaterialApi::class)
@ExperimentalCoilApi
@@ -104,8 +100,6 @@ class DashboardScreen(
Emarsys.config.changeMerchantId(
merchantId = viewModel.getTfMerchantIdValue(),
)
- SetupInfo.merchantId = viewModel.getTfMerchantIdValue()
- SetupInfo.notifyObservers()
customTextToast(
context,
context.getString(R.string.merchant_id_change_success)
@@ -130,7 +124,7 @@ class DashboardScreen(
Text(text = stringResource(id = R.string.enable))
Switch(
checked = viewModel.isGeofenceEnabled(),
- onCheckedChange = { enabled ->
+ onCheckedChange = { isEnabled ->
if (!viewModel.isGeofenceEnabled()) {
Emarsys.geofence.enable {
if (it != null) {
@@ -140,7 +134,7 @@ class DashboardScreen(
context.getString(R.string.something_went_wrong)
)
} else {
- viewModel.geofenceEnabled.value = enabled
+ viewModel.geofenceEnabled.value = isEnabled
customTextToast(
context,
context.getString(R.string.geofence_enabled)
@@ -149,7 +143,7 @@ class DashboardScreen(
}
} else {
Emarsys.geofence.disable()
- viewModel.geofenceEnabled.value = enabled
+ viewModel.geofenceEnabled.value = isEnabled
customTextToast(context, context.getString(R.string.geofence_disabled))
}
}
@@ -208,7 +202,7 @@ class DashboardScreen(
account.idToken!!
) {
if (verifyLogin(it, context)) {
- setupInfoSetContact(context)
+ setContact(context)
}
}
}
@@ -216,42 +210,28 @@ class DashboardScreen(
private fun onChangeAppCodeClicked(somethingWentWrong: String, appCodeChangeSuccess: String) {
Emarsys.config.changeApplicationCode(
- applicationCode = viewModel.getTfAppCodeValue(),
- completionListener = { changeError ->
- if (viewModel.shouldChangeEnv()) {
- if (changeError != null) {
- customTextToast(context, somethingWentWrong)
- }
- exitProcess(0)
- } else if (changeError != null) {
- customTextToast(context, somethingWentWrong)
- } else {
- if (viewModel.hasLogin()) {
- setupInfoClearContact()
- customTextToast(context, appCodeChangeSuccess)
- } else {
- (application as SampleApplication).setupEventHandlers()
- customTextToast(context, appCodeChangeSuccess)
- }
- viewModel.resetContactInfo()
- }
+ applicationCode = viewModel.getTfAppCodeValue()
+ ) { changeError ->
+ if (changeError != null) {
+ customTextToast(context, somethingWentWrong)
+ } else {
+ viewModel.clearContact()
+ customTextToast(context, appCodeChangeSuccess)
}
- )
- SetupInfo.applicationCode = viewModel.getTfAppCodeValue()
- SetupInfo.notifyObservers()
+ if (viewModel.shouldChangeEnv()) {
+ exitProcess(0)
+ }
+ }
}
private fun onLoginClicked() {
- if (!viewModel.hasLogin()) {
- if (viewModel.isContactDataPresent()
+ if (!viewModel.hasLogin() && viewModel.isContactDataPresent()) {
+ Emarsys.setContact(
+ contactFieldId = viewModel.getTfContactFieldIdValue().toInt(),
+ contactFieldValue = viewModel.getTfContactFieldValue()
) {
- Emarsys.setContact(
- contactFieldId = viewModel.getTfContactFieldIdValue().toInt(),
- contactFieldValue = viewModel.getTfContactFieldValue()
- ) {
- if (verifyLogin(it, context)) {
- setupInfoSetContact(context)
- }
+ if (verifyLogin(it, context)) {
+ setContact(context)
}
}
} else {
@@ -259,8 +239,7 @@ class DashboardScreen(
if (it != null) {
customTextToast(context, context.getString(R.string.log_out_fail))
} else {
- setupInfoClearContact()
- viewModel.resetContactInfo()
+ viewModel.clearContact()
customTextToast(context, context.getString(R.string.log_out_success))
}
}
@@ -281,18 +260,11 @@ class DashboardScreen(
}
}
- private fun setupInfoClearContact() {
- SetupInfo.contactFieldId = 0.toString()
- SetupInfo.contactFieldValue = ""
- SetupInfo.loggedIn = false
- SetupInfo.notifyObservers()
- }
-
- private fun setupInfoSetContact(context: Context) {
- SetupInfo.contactFieldValue = viewModel.getTfContactFieldValue()
- SetupInfo.contactFieldId = viewModel.getTfContactFieldIdValue()
- SetupInfo.loggedIn = true
- SetupInfo.notifyObservers()
+ private fun setContact(context: Context) {
+ viewModel.setContact(
+ fieldId = viewModel.getTfContactFieldIdValue(),
+ fieldValue = viewModel.getTfContactFieldValue()
+ )
viewModel.isLoggedIn.value = true
trackPushToken(context)
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardViewModel.kt b/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardViewModel.kt
index 63e764fb..5c74c734 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardViewModel.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/dashboard/DashboardViewModel.kt
@@ -5,6 +5,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.emarsys.Emarsys
import com.emarsys.sample.pref.Prefs
+import com.emarsys.sample.pref.update
class DashboardViewModel : ViewModel() {
@@ -30,18 +31,10 @@ class DashboardViewModel : ViewModel() {
return this.tfContactFieldId.value
}
- fun setTfContactFieldId(value: String) {
- this.tfContactFieldId.value = value
- }
-
fun getTfContactFieldValue(): String {
return this.tfContactFieldValue.value
}
- fun setTfContactField(value: String) {
- this.tfContactFieldValue.value = value
- }
-
fun hasLogin(): Boolean {
return this.isLoggedIn.value
}
@@ -66,12 +59,6 @@ class DashboardViewModel : ViewModel() {
return this.geofenceEnabled.value
}
- fun resetContactInfo() {
- this.setTfContactField("")
- this.setTfContactFieldId("0")
- this.isLoggedIn.value = false
- }
-
fun isContactDataPresent(): Boolean {
return if (
this.getTfContactFieldValue().isNotEmpty() &&
@@ -97,4 +84,31 @@ class DashboardViewModel : ViewModel() {
fun isErrorVisible(): Boolean {
return this.errorVisible.value
}
+
+ fun getInfoMap(): Map {
+ return mapOf(
+ "loggedIn" to isLoggedIn.value.toString(),
+ "applicationCode" to tfAppCode.value,
+ "merchantId" to tfMerchantId.value,
+ "hardwareId" to Prefs.hardwareId,
+ "languageCode" to Prefs.languageCode,
+ "sdkVersion" to Prefs.sdkVersion,
+ "contactFieldValue" to tfContactFieldValue.value,
+ "contactFieldId" to tfContactFieldId.value
+ )
+ }
+
+ fun clearContact() {
+ tfContactFieldId.value = "0"
+ tfContactFieldValue.value = ""
+ isLoggedIn.value = false
+ Prefs.update(getInfoMap())
+ }
+
+ fun setContact(fieldId: String, fieldValue: String) {
+ tfContactFieldId.value = fieldId
+ tfContactFieldValue.value = fieldValue
+ isLoggedIn.value = true
+ Prefs.update(getInfoMap())
+ }
}
\ No newline at end of file
diff --git a/sample/src/main/kotlin/com/emarsys/sample/inapp/InAppScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/inapp/InAppScreen.kt
index 04dcf072..7e4704f0 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/inapp/InAppScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/inapp/InAppScreen.kt
@@ -1,11 +1,9 @@
package com.emarsys.sample.inapp
-import android.app.Application
import android.content.Context
import android.util.Log
import android.view.View
import android.widget.Toast
-import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
@@ -46,8 +44,7 @@ import com.emarsys.sample.ui.style.rowWithPointEightWidth
import java.util.UUID
class InAppScreen(
- override val context: Context,
- override val application: Application
+ override val context: Context
) : DetailScreen() {
private val trackCustomEvent = {
if (viewModel.isEventPresent()) {
@@ -71,7 +68,6 @@ class InAppScreen(
private val viewModel = InAppViewModel()
- @OptIn(ExperimentalAnimationApi::class)
@ExperimentalCoilApi
@ExperimentalComposeUiApi
@Composable
diff --git a/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxScreen.kt
index 3c1dce4b..a6193313 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxScreen.kt
@@ -1,16 +1,18 @@
package com.emarsys.sample.inbox
-import android.app.Application
import android.content.Context
import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.Scaffold
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
-import androidx.compose.runtime.*
+import androidx.compose.material.pullrefresh.PullRefreshIndicator
+import androidx.compose.material.pullrefresh.pullRefresh
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
+import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -20,72 +22,59 @@ import com.emarsys.sample.R
import com.emarsys.sample.ui.component.row.RowWithCenteredContent
import com.emarsys.sample.ui.component.screen.DetailScreen
import com.emarsys.sample.ui.component.text.TitleText
-import com.emarsys.sample.ui.style.columnWithMaxWidth
-import com.google.accompanist.swiperefresh.SwipeRefresh
-import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
-import kotlinx.coroutines.delay
+import com.emarsys.sample.ui.style.rowWithMaxWidth
+@ExperimentalCoilApi
+@ExperimentalMaterialApi
+@ExperimentalComposeUiApi
class InboxScreen(
- override val context: Context,
- override val application: Application
+ override val context: Context
) : DetailScreen() {
-
private val viewModel = InboxViewModel()
- private val messagePresenter = MessagePresenter(context, viewModel)
+ private val messagePresenter = MessagePresenter(context)
- @ExperimentalCoilApi
- @ExperimentalComposeUiApi
@Composable
override fun Detail(paddingValues: PaddingValues) {
- SwipeRefreshCompose(messagePresenter = messagePresenter, paddingValues)
- }
-
- @OptIn(ExperimentalCoilApi::class, androidx.compose.animation.ExperimentalAnimationApi::class)
- @Composable
- private fun SwipeRefreshCompose(
- messagePresenter: MessagePresenter,
- innerPadding: PaddingValues
- ) {
- Scaffold(
- Modifier
- .columnWithMaxWidth(),
- topBar = {
- TitleText(titleText = stringResource(id = R.string.inbox_title))
- })
+ val pullRefreshState = rememberPullRefreshState(
+ refreshing = viewModel.refreshing.value,
+ onRefresh = viewModel::onSwipeFetchMessages
+ )
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .rowWithMaxWidth()
+ .pullRefresh(pullRefreshState),
+ )
{
- it.calculateBottomPadding()
- var refreshing by remember { mutableStateOf(false) }
- LaunchedEffect(key1 = refreshing) {
- if (refreshing) {
- delay(1000)
- refreshing = false
- }
- }
- SwipeRefresh(
- state = rememberSwipeRefreshState(isRefreshing = refreshing),
- onRefresh = {
- refreshing = true
- viewModel.onSwipeFetchMessages()
- })
- {
- AnimatedVisibility(visible = viewModel.isFetchedMessagesEmpty()) {
- RowWithCenteredContent {
- Text(text = stringResource(id = R.string.pull_down))
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize(1f),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ item {
+ RowWithCenteredContent() {
+ TitleText(titleText = stringResource(id = R.string.inbox_title))
}
}
- LazyColumn(
- modifier = Modifier
- .fillMaxSize(1f)
- .padding(bottom = innerPadding.calculateBottomPadding()),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- items(
- items = viewModel.fetchedMessages.toList(),
- key = { message -> message.id }) { message ->
- messagePresenter.MessageCard(message = message)
+ item {
+ AnimatedVisibility(visible = viewModel.isFetchedMessagesEmpty()) {
+ RowWithCenteredContent {
+ Text(text = stringResource(id = R.string.pull_down))
+ }
}
}
+ items(
+ items = viewModel.fetchedMessages.toList(),
+ key = { message -> message.id }) { message ->
+ messagePresenter.MessageCard(message = message)
+ }
}
+
+ PullRefreshIndicator(
+ refreshing = viewModel.refreshing.value,
+ state = pullRefreshState,
+ modifier = Modifier.align(Alignment.TopCenter)
+ )
}
}
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxViewModel.kt b/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxViewModel.kt
index 8ae2d56d..9c4b218c 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxViewModel.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/inbox/InboxViewModel.kt
@@ -1,13 +1,14 @@
package com.emarsys.sample.inbox
import android.util.Log
+import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.emarsys.Emarsys
import com.emarsys.inbox.InboxTag
import com.emarsys.mobileengage.api.inbox.Message
class InboxViewModel : ViewModel() {
-
+ val refreshing = mutableStateOf(false)
val fetchedMessages = mutableSetOf()
fun isFetchedMessagesEmpty(): Boolean {
@@ -19,6 +20,7 @@ class InboxViewModel : ViewModel() {
}
fun onSwipeFetchMessages() {
+ refreshing.value = true
Emarsys.messageInbox.fetchMessages {
if (it.errorCause != null) {
Log.e("INBOX", "Inbox Error" + it.errorCause)
@@ -32,9 +34,10 @@ class InboxViewModel : ViewModel() {
messageId = message.id
)
}
- this.addMessageToFetched(message)
+ addMessageToFetched(message)
}
}
+ refreshing.value = false
}
}
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/inbox/MessagePresenter.kt b/sample/src/main/kotlin/com/emarsys/sample/inbox/MessagePresenter.kt
index 8597995c..483323c0 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/inbox/MessagePresenter.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/inbox/MessagePresenter.kt
@@ -6,7 +6,11 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Card
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@@ -18,6 +22,7 @@ import com.emarsys.mobileengage.api.inbox.Message
import com.emarsys.sample.R
import com.emarsys.sample.inbox.component.InboxButton
import com.emarsys.sample.inbox.event.InboxAppEventHandler
+import com.emarsys.sample.ui.cardWithFullWidth
import com.emarsys.sample.ui.component.button.StyledTextButton
import com.emarsys.sample.ui.component.divider.DividerWithBackgroundColor
import com.emarsys.sample.ui.component.row.RowWithCenteredContent
@@ -26,11 +31,10 @@ import com.emarsys.sample.ui.component.text.TextWithFullWidthLine
import com.emarsys.sample.ui.component.text.TitleText
import com.emarsys.sample.ui.component.textfield.StyledTextField
import com.emarsys.sample.ui.component.toast.customTextToast
-import com.emarsys.sample.ui.style.cardWithFullWidth
import com.emarsys.sample.ui.style.columnWithMaxWidth
import com.emarsys.sample.ui.style.rowWithMaxWidth
-class MessagePresenter(private val context: Context, private val viewModel: InboxViewModel) {
+class MessagePresenter(private val context: Context) {
private val inboxAppEventHandler = InboxAppEventHandler()
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/MainActivity.kt b/sample/src/main/kotlin/com/emarsys/sample/main/MainActivity.kt
index 5407e684..33fc46dc 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/MainActivity.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/MainActivity.kt
@@ -1,39 +1,43 @@
package com.emarsys.sample.main
-import android.app.Application
-import android.content.Context
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Scaffold
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
import androidx.fragment.app.FragmentActivity
+import coil.annotation.ExperimentalCoilApi
+import com.emarsys.sample.dashboard.DashboardViewModel
+import com.emarsys.sample.main.navigation.BottomNavigationBar
import com.emarsys.sample.main.navigation.NavigationControllerProvider
import com.emarsys.sample.main.sdkinfo.TopExpandableCard
-import com.emarsys.sample.ui.component.navbar.BottomNavigationBar
import com.emarsys.sample.ui.theme.AndroidSampleAppTheme
class MainActivity : FragmentActivity() {
- @OptIn(ExperimentalAnimationApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class,
- coil.annotation.ExperimentalCoilApi::class
- )
+ @ExperimentalAnimationApi
+ @ExperimentalComposeUiApi
+ @ExperimentalCoilApi
+ @ExperimentalMaterialApi
@RequiresApi(Build.VERSION_CODES.Q)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val application: Application = this.application
- val context: Context = this.applicationContext
- val mainViewModel = MainViewModel(context, application)
+ val dashboardViewModel = DashboardViewModel()
+ val mainViewModel = MainViewModel(this.applicationContext, dashboardViewModel)
val navControllerProvider = NavigationControllerProvider(mainViewModel)
setContent {
val navHostController = navControllerProvider.provide()
AndroidSampleAppTheme {
- Scaffold(
+ Scaffold(modifier = Modifier.imePadding(),
topBar = {
- TopExpandableCard().TopExpandableCard(context)
+ TopExpandableCard(this.applicationContext, dashboardViewModel)
},
bottomBar = {
BottomNavigationBar(navController = navHostController)
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/MainViewModel.kt b/sample/src/main/kotlin/com/emarsys/sample/main/MainViewModel.kt
index fdabef0f..86d97a24 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/MainViewModel.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/MainViewModel.kt
@@ -1,22 +1,29 @@
package com.emarsys.sample.main
-import android.app.Application
import android.content.Context
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.lifecycle.ViewModel
-import com.emarsys.sample.ui.component.screen.DetailScreen
+import coil.annotation.ExperimentalCoilApi
import com.emarsys.sample.dashboard.DashboardScreen
+import com.emarsys.sample.dashboard.DashboardViewModel
import com.emarsys.sample.inapp.InAppScreen
import com.emarsys.sample.inbox.InboxScreen
import com.emarsys.sample.mobileengage.MobileEngageScreen
import com.emarsys.sample.predict.PredictScreen
+import com.emarsys.sample.ui.component.screen.DetailScreen
-class MainViewModel(context: Context, application: Application) : ViewModel() {
- val dashBoardScreen = DashboardScreen(context, application)
- val mobileEngageScreen = MobileEngageScreen(context, application)
- val inboxScreen = InboxScreen(context, application)
- val predictScreen = PredictScreen(context, application)
- val inAppScreen = InAppScreen(context, application)
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterialApi::class, ExperimentalCoilApi::class)
+class MainViewModel(
+ context: Context,
+ dashboardViewModel: DashboardViewModel
+) : ViewModel() {
+ val dashBoardScreen = DashboardScreen(context, dashboardViewModel)
+ val mobileEngageScreen = MobileEngageScreen(context)
+ val inboxScreen = InboxScreen(context)
+ val predictScreen = PredictScreen(context)
+ val inAppScreen = InAppScreen(context)
val detailScreen = mutableStateOf(
dashBoardScreen
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/BottomNavigationBar.kt b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/BottomNavigationBar.kt
index 403a47f9..b552efbf 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/BottomNavigationBar.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/BottomNavigationBar.kt
@@ -1,4 +1,4 @@
-package com.emarsys.sample.ui.component.navbar
+package com.emarsys.sample.main.navigation
import androidx.compose.material.BottomNavigation
@@ -12,17 +12,16 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
-import com.emarsys.sample.main.navigation.NavigationbarItem
import com.emarsys.sample.ui.style.rowWithMaxWidth
@Composable
fun BottomNavigationBar(navController: NavController) {
val navItems = listOf(
- NavigationbarItem.BottomDashBoard,
- NavigationbarItem.BottomMobileEngage,
- NavigationbarItem.BottomInbox,
- NavigationbarItem.BottomPredict,
- NavigationbarItem.BottomInApp
+ NavigationBarItem.BottomDashBoard,
+ NavigationBarItem.BottomMobileEngage,
+ NavigationBarItem.BottomInbox,
+ NavigationBarItem.BottomPredict,
+ NavigationBarItem.BottomInApp
)
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationBarItem.kt b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationBarItem.kt
new file mode 100644
index 00000000..f7e4761a
--- /dev/null
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationBarItem.kt
@@ -0,0 +1,11 @@
+package com.emarsys.sample.main.navigation
+
+import com.emarsys.sample.R
+
+sealed class NavigationBarItem(var route: String, var icon: Int, var title: String) {
+ data object BottomDashBoard : NavigationBarItem("dashboard", R.drawable.ic_settings, "Dashboard")
+ data object BottomMobileEngage : NavigationBarItem("mobile-engage", R.drawable.mobile_engage_logo_icon, "Mobile Engage")
+ data object BottomInbox : NavigationBarItem("inbox", R.drawable.inbox_mailbox_icon, "Inbox")
+ data object BottomPredict : NavigationBarItem("predict", R.drawable.predict_scarab_icon, "Predict")
+ data object BottomInApp : NavigationBarItem("inapp", R.drawable.mobile_engage_logo_icon, "InApp")
+}
\ No newline at end of file
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationControllerProvider.kt b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationControllerProvider.kt
index b6362672..20c71ac9 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationControllerProvider.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationControllerProvider.kt
@@ -1,23 +1,29 @@
package com.emarsys.sample.main.navigation
+import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
+import coil.annotation.ExperimentalCoilApi
import com.emarsys.sample.main.MainViewModel
class NavigationControllerProvider(private val mainViewModel: MainViewModel) {
+ @ExperimentalMaterialApi
+ @ExperimentalComposeUiApi
+ @ExperimentalCoilApi
@Composable
fun provide(): NavHostController {
val navHostController = rememberNavController()
- NavHost(navHostController, startDestination = "bottom-dashboard") {
- composable("bottom-dashboard") { mainViewModel.detailScreen.value = mainViewModel.dashBoardScreen }
- composable("bottom-mobile-engage") { mainViewModel.detailScreen.value = mainViewModel.mobileEngageScreen }
- composable("bottom-inbox") { mainViewModel.detailScreen.value = mainViewModel.inboxScreen }
- composable("bottom-predict") { mainViewModel.detailScreen.value = mainViewModel.predictScreen }
- composable("bottom-inapp") { mainViewModel.detailScreen.value = mainViewModel.inAppScreen }
+ NavHost(navHostController, startDestination = "dashboard") {
+ composable("dashboard") { mainViewModel.detailScreen.value = mainViewModel.dashBoardScreen }
+ composable("mobile-engage") { mainViewModel.detailScreen.value = mainViewModel.mobileEngageScreen }
+ composable("inbox") { mainViewModel.detailScreen.value = mainViewModel.inboxScreen }
+ composable("predict") { mainViewModel.detailScreen.value = mainViewModel.predictScreen }
+ composable("inapp") { mainViewModel.detailScreen.value = mainViewModel.inAppScreen }
}
return navHostController
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationbarItem.kt b/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationbarItem.kt
deleted file mode 100644
index 62ef675a..00000000
--- a/sample/src/main/kotlin/com/emarsys/sample/main/navigation/NavigationbarItem.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.emarsys.sample.main.navigation
-
-import com.emarsys.sample.R
-
-sealed class NavigationbarItem(var route: String, var icon: Int, var title: String) {
- object BottomDashBoard : NavigationbarItem("bottom-dashboard", R.drawable.ic_settings, "Dashboard")
- object BottomMobileEngage : NavigationbarItem("bottom-mobile-engage", R.drawable.mobile_engage_logo_icon, "Mobile Engage")
- object BottomInbox : NavigationbarItem("bottom-inbox", R.drawable.inbox_mailbox_icon, "Inbox")
- object BottomPredict : NavigationbarItem("bottom-predict", R.drawable.predict_scarab_icon, "Predict")
- object BottomInApp : NavigationbarItem("bottom-inapp", R.drawable.mobile_engage_logo_icon, "InApp")
-}
\ No newline at end of file
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/SetupInfo.kt b/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/SetupInfo.kt
deleted file mode 100644
index d73f6b83..00000000
--- a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/SetupInfo.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.emarsys.sample.main.sdkinfo
-
-import androidx.compose.runtime.mutableStateMapOf
-import com.emarsys.Emarsys
-import com.emarsys.sample.pref.Prefs
-import java.util.*
-
-object SetupInfo : Observable() {
- var loggedIn: Boolean = Prefs.loggedIn
- var hardwareId: String = Emarsys.config.hardwareId
- var languageCode: String = Emarsys.config.languageCode
- var sdkVersion: String = Emarsys.config.sdkVersion
- var contactFieldValue: String = Prefs.contactFieldValue
- var contactFieldId: String = Prefs.contactFieldId.toString()
- var applicationCode: String = Prefs.applicationCode
- var merchantId: String = Prefs.merchantId
-
- private val observers = listOf(TopCardViewModel, Prefs)
-
- override fun addObserver(o: Observer?) {
- super.addObserver(o)
- }
-
- override fun notifyObservers() {
- observers.forEach { observer ->
- observer.update(this, provideInfoMap())
- }
- }
-
- fun provideInfoMap(): MutableMap {
- return mutableStateMapOf(
- "LoggedIn" to loggedIn.toString(),
- "ApplicationCode" to applicationCode,
- "MerchantId" to merchantId,
- "HardwareId" to hardwareId,
- "LanguageCode" to languageCode,
- "SdkVersion" to sdkVersion,
- "ContactFieldValue" to contactFieldValue,
- "ContactFieldId" to contactFieldId
- )
- }
-}
\ No newline at end of file
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopCardViewModel.kt b/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopCardViewModel.kt
index ff5ed509..efa9c00e 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopCardViewModel.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopCardViewModel.kt
@@ -2,10 +2,8 @@ package com.emarsys.sample.main.sdkinfo
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
-import java.util.*
-object TopCardViewModel : ViewModel(), Observer {
- val setupInfo = mutableStateOf(SetupInfo.provideInfoMap())
+object TopCardViewModel : ViewModel() {
val expanded = mutableStateOf(false)
fun getMoreLessText(): String {
@@ -15,8 +13,4 @@ object TopCardViewModel : ViewModel(), Observer {
fun toggleCardExpansion() {
expanded.value = !expanded.value
}
-
- override fun update(o: Observable?, arg: Any?) {
- setupInfo.value = arg as MutableMap
- }
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopExpandableCard.kt b/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopExpandableCard.kt
index 8a1a0cfe..dc679227 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopExpandableCard.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/main/sdkinfo/TopExpandableCard.kt
@@ -7,7 +7,11 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@@ -18,88 +22,97 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
-import com.emarsys.sample.ui.style.cardWithFullWidth
-import com.emarsys.sample.ui.style.columnWithMaxWidth
+import com.emarsys.sample.dashboard.DashboardViewModel
+import com.emarsys.sample.ui.cardWithFullWidth
import com.emarsys.sample.ui.component.toast.customTextToast
+import com.emarsys.sample.ui.style.columnWithMaxWidth
-class TopExpandableCard {
-
- @ExperimentalAnimationApi
- @Composable
- fun TopExpandableCard(context: Context) {
- Card(modifier = Modifier
- .clickable {
- TopCardViewModel.toggleCardExpansion()
- }
- .cardWithFullWidth(),
- elevation = 8.dp
- ) {
- AnimatedVisibility(visible = !TopCardViewModel.expanded.value) {
- LoginStatus(itemsToList = TopCardViewModel.setupInfo.value)
- MoreLessText(textToShow = TopCardViewModel.getMoreLessText())
- }
- AnimatedVisibility(visible = TopCardViewModel.expanded.value) {
- Column(
- modifier = Modifier
- .fillMaxWidth(1f)
- .padding(start = 4.dp)
- ) {
- SettingsStatus(context, TopCardViewModel.setupInfo.value)
- Row(horizontalArrangement = Arrangement.End) {
- MoreLessText(textToShow = TopCardViewModel.getMoreLessText())
- }
+@ExperimentalAnimationApi
+@Composable
+fun TopExpandableCard(context: Context, dashboardViewModel: DashboardViewModel) {
+ Card(modifier = Modifier
+ .clickable {
+ TopCardViewModel.toggleCardExpansion()
+ }
+ .cardWithFullWidth(),
+ elevation = 8.dp
+ ) {
+ AnimatedVisibility(visible = !TopCardViewModel.expanded.value) {
+ LoginStatus(dashboardViewModel.hasLogin())
+ MoreLessText(textToShow = TopCardViewModel.getMoreLessText())
+ }
+ AnimatedVisibility(visible = TopCardViewModel.expanded.value) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth(1f)
+ .padding(start = 4.dp)
+ ) {
+ SettingsStatus(context, dashboardViewModel.getInfoMap())
+ Row(horizontalArrangement = Arrangement.End) {
+ MoreLessText(textToShow = TopCardViewModel.getMoreLessText())
}
}
}
}
+}
- @Composable
- private fun LoginStatus(itemsToList: MutableMap) {
- Column(
- modifier = Modifier.columnWithMaxWidth()
- ) {
- Row { Text(text = "Logged in: " + itemsToList["LoggedIn"].toString()) }
+@Composable
+private fun LoginStatus(isLoggedIn: Boolean) {
+ Column(
+ modifier = Modifier
+ .columnWithMaxWidth()
+ .padding(4.dp)
+ ) {
+ Row {
+ Text(
+ text = "Logged in: $isLoggedIn",
+ style = MaterialTheme.typography.body1
+ )
}
}
+}
- @Composable
- private fun MoreLessText(textToShow: String) {
- Text(
- text = textToShow,
- modifier = Modifier
- .fillMaxWidth(1f),
- textAlign = TextAlign.End
- )
- }
+@Composable
+private fun MoreLessText(textToShow: String) {
+ Text(
+ text = textToShow,
+ style = MaterialTheme.typography.body1,
+ modifier = Modifier
+ .fillMaxWidth(1f)
+ .padding(4.dp),
+ textAlign = TextAlign.End
+ )
+}
- @Composable
- private fun SettingsStatus(context: Context, itemsToList: MutableMap) {
- itemsToList.forEach { item ->
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceEvenly
- ) {
- Text(text = "${item.key}: ")
- Text(
- text = item.value,
- style = MaterialTheme.typography.caption,
- modifier = Modifier.pointerInput(Unit) {
- detectTapGestures(onTap = {
- copyToClipboard(context, item.value)
- })
- }
- )
- }
+@Composable
+private fun SettingsStatus(context: Context, itemsToList: Map) {
+ itemsToList.forEach { item ->
+ Row(
+ modifier = Modifier.padding(4.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Text(text = "${item.key}: ", style = MaterialTheme.typography.body1)
+ Text(
+ text = item.value,
+ style = MaterialTheme.typography.body1,
+ modifier = Modifier.pointerInput(Unit) {
+ detectTapGestures(onTap = {
+ copyToClipboard(context, item.value)
+ })
+ }
+ )
}
}
- private fun copyToClipboard(context: Context, copy: String) {
- val myClipboard =
- ContextCompat.getSystemService(
- context,
- ClipboardManager::class.java
- ) as ClipboardManager
- val clip = ClipData.newPlainText("copied text", copy)
- myClipboard.setPrimaryClip(clip)
- customTextToast(context, "Copied to clipboard, value: $copy")
- }
+}
+
+private fun copyToClipboard(context: Context, copy: String) {
+ val myClipboard =
+ ContextCompat.getSystemService(
+ context,
+ ClipboardManager::class.java
+ ) as ClipboardManager
+ val clip = ClipData.newPlainText("copied text", copy)
+ myClipboard.setPrimaryClip(clip)
+ customTextToast(context, "Copied to clipboard, value: $copy")
}
diff --git a/sample/src/main/kotlin/com/emarsys/sample/mobileengage/MobileEngageScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/mobileengage/MobileEngageScreen.kt
index eb454f62..6e48f8ed 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/mobileengage/MobileEngageScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/mobileengage/MobileEngageScreen.kt
@@ -1,6 +1,5 @@
package com.emarsys.sample.mobileengage
-import android.app.Application
import android.content.Context
import android.util.Log
import android.widget.Toast
@@ -19,19 +18,18 @@ import coil.annotation.ExperimentalCoilApi
import com.emarsys.Emarsys
import com.emarsys.sample.R
import com.emarsys.sample.ui.component.ColumnWithTapGesture
-import com.emarsys.sample.ui.component.screen.DetailScreen
import com.emarsys.sample.ui.component.button.StyledTextButton
import com.emarsys.sample.ui.component.divider.DividerWithBackgroundColor
import com.emarsys.sample.ui.component.divider.GreyLine
+import com.emarsys.sample.ui.component.screen.DetailScreen
import com.emarsys.sample.ui.component.text.TitleText
import com.emarsys.sample.ui.component.textfield.EventPayloadTextArea
import com.emarsys.sample.ui.component.textfield.StyledTextField
-import com.emarsys.sample.ui.style.rowWithPointEightWidth
import com.emarsys.sample.ui.component.toast.ErrorDialog
+import com.emarsys.sample.ui.style.rowWithPointEightWidth
class MobileEngageScreen(
- override val context: Context,
- override val application: Application
+ override val context: Context
) : DetailScreen() {
private val viewModel = MobileEngageViewModel()
private val trackCustomEvent = {
diff --git a/sample/src/main/kotlin/com/emarsys/sample/predict/PredictScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/predict/PredictScreen.kt
index 475ad4ba..06150433 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/predict/PredictScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/predict/PredictScreen.kt
@@ -1,6 +1,5 @@
package com.emarsys.sample.predict
-import android.app.Application
import android.content.Context
import android.util.Log
import androidx.compose.foundation.gestures.detectTapGestures
@@ -23,20 +22,19 @@ import androidx.compose.ui.res.stringResource
import coil.annotation.ExperimentalCoilApi
import com.emarsys.Emarsys
import com.emarsys.sample.R
-import com.emarsys.sample.ui.component.button.StyledTextButton
import com.emarsys.sample.predict.cart.SampleCartItemCard
+import com.emarsys.sample.ui.component.button.StyledTextButton
import com.emarsys.sample.ui.component.divider.GreyLine
+import com.emarsys.sample.ui.component.screen.DetailScreen
import com.emarsys.sample.ui.component.text.TitleText
+import com.emarsys.sample.ui.component.toast.ErrorDialog
+import com.emarsys.sample.ui.component.toast.customTextToast
import com.emarsys.sample.ui.style.columnWithMaxWidth
import com.emarsys.sample.ui.style.rowWithMaxWidth
import com.emarsys.sample.ui.style.rowWithPointEightWidth
-import com.emarsys.sample.ui.component.screen.DetailScreen
-import com.emarsys.sample.ui.component.toast.ErrorDialog
-import com.emarsys.sample.ui.component.toast.customTextToast
class PredictScreen(
- override val context: Context,
- override val application: Application
+ override val context: Context
) : DetailScreen() {
private val viewModel = PredictViewModel()
diff --git a/sample/src/main/kotlin/com/emarsys/sample/predict/RecommendedProductsCard.kt b/sample/src/main/kotlin/com/emarsys/sample/predict/RecommendedProductsCard.kt
index 6edb0e78..f42ce25e 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/predict/RecommendedProductsCard.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/predict/RecommendedProductsCard.kt
@@ -10,7 +10,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.emarsys.predict.api.model.Product
import com.emarsys.sample.R
-import com.emarsys.sample.ui.style.cardWithPointEightWidth
+import com.emarsys.sample.ui.cardWithPointEightWidth
import com.emarsys.sample.ui.style.columnWithPointEightWidth
import com.emarsys.sample.ui.style.rowWithPointEightWidth
diff --git a/sample/src/main/kotlin/com/emarsys/sample/predict/cart/SampleCartItemCard.kt b/sample/src/main/kotlin/com/emarsys/sample/predict/cart/SampleCartItemCard.kt
index 88f5d494..7f62ded8 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/predict/cart/SampleCartItemCard.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/predict/cart/SampleCartItemCard.kt
@@ -8,7 +8,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import com.emarsys.sample.ui.style.cardWithPointEightWidth
+import com.emarsys.sample.ui.cardWithPointEightWidth
import com.emarsys.sample.ui.style.rowWithPointEightWidth
@Composable
diff --git a/sample/src/main/kotlin/com/emarsys/sample/pref/Prefs.kt b/sample/src/main/kotlin/com/emarsys/sample/pref/Prefs.kt
index a7ada5d0..c1b4c765 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/pref/Prefs.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/pref/Prefs.kt
@@ -1,9 +1,9 @@
package com.emarsys.sample.pref
import com.chibatching.kotpref.KotprefModel
-import java.util.*
-object Prefs : KotprefModel(), Observer {
+object Prefs : KotprefModel() {
+ override val commitAllPropertiesByDefault: Boolean = true
var sdkVersion by stringPref("")
var languageCode by stringPref("")
@@ -13,17 +13,15 @@ object Prefs : KotprefModel(), Observer {
var contactFieldValue by stringPref("")
var contactFieldId by intPref(0)
var loggedIn by booleanPref(false)
+}
- override fun update(o: Observable?, arg: Any?) {
- val newSettings = arg as MutableMap
-
- loggedIn = newSettings["LoggedIn"]!!.toBoolean()
- applicationCode = newSettings["ApplicationCode"]!!
- merchantId = newSettings["MerchantId"]!!
- hardwareId = newSettings["HardwareId"]!!
- languageCode = newSettings["LanguageCode"]!!
- sdkVersion = newSettings["SdkVersion"]!!
- contactFieldValue = newSettings["ContactFieldValue"]!!
- contactFieldId = newSettings["ContactFieldId"]!!.toInt()
- }
+fun Prefs.update(sdkInfo: Map) {
+ sdkVersion = sdkInfo.getOrDefault("sdkVersion", "")
+ languageCode = sdkInfo.getOrDefault("languageCode", "")
+ hardwareId = sdkInfo.getOrDefault("hardwareId", "")
+ applicationCode = sdkInfo.getOrDefault("applicationCode", "")
+ merchantId = sdkInfo.getOrDefault("merchantId", "")
+ contactFieldValue = sdkInfo.getOrDefault("contactFieldValue", "")
+ contactFieldId = sdkInfo.getOrDefault("contactFieldId", "0").toInt()
+ loggedIn = sdkInfo.getOrDefault("loggedIn", "false").toBoolean()
}
\ No newline at end of file
diff --git a/sample/src/main/kotlin/com/emarsys/sample/ui/Card.kt b/sample/src/main/kotlin/com/emarsys/sample/ui/Card.kt
index 48793736..09ad14ea 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/ui/Card.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/ui/Card.kt
@@ -1,4 +1,4 @@
-package com.emarsys.sample.ui.style
+package com.emarsys.sample.ui
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
diff --git a/sample/src/main/kotlin/com/emarsys/sample/ui/component/screen/DetailScreen.kt b/sample/src/main/kotlin/com/emarsys/sample/ui/component/screen/DetailScreen.kt
index 9a4d8d53..3550abfd 100644
--- a/sample/src/main/kotlin/com/emarsys/sample/ui/component/screen/DetailScreen.kt
+++ b/sample/src/main/kotlin/com/emarsys/sample/ui/component/screen/DetailScreen.kt
@@ -1,6 +1,5 @@
package com.emarsys.sample.ui.component.screen
-import android.app.Application
import android.content.Context
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
@@ -10,11 +9,9 @@ import coil.annotation.ExperimentalCoilApi
abstract class DetailScreen {
abstract val context: Context
- abstract val application: Application
@ExperimentalCoilApi
@ExperimentalComposeUiApi
@Composable
- open fun Detail(paddingValues: PaddingValues) {
- }
+ abstract fun Detail(paddingValues: PaddingValues)
}
\ No newline at end of file