Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.5.0] - 2026-02-19

### Added

- `reset()` method to clear all local SDK state including device ID

### Deprecated

- `removeUserId()` — use `reset()` instead

## [1.4.1] - 2026-02-13

### Added
Expand Down
20 changes: 20 additions & 0 deletions clix/src/main/kotlin/so/clix/core/Clix.kt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ object Clix {
}

/** Removes the user ID for the current user. */
@Deprecated("Use reset() instead", ReplaceWith("reset()"))
@JvmStatic
suspend fun removeUserId() {
try {
Expand All @@ -151,6 +152,25 @@ object Clix {
}
}

/**
* Resets all local SDK state including device ID.
*
* After calling this method, you must call [initialize] again before using the SDK. Use this
* when a user logs out and you want to start fresh with a new device identity.
*/
@JvmStatic
fun reset() {
try {
notificationService.reset()
sessionService?.stop()
storageService.remove("clix_device_id")
storageService.remove("clix_session_last_activity")
isInitialized = false
} catch (e: Exception) {
ClixLogger.error("Failed to reset", e)
}
}

/**
* Sets a single user property.
*
Expand Down
4 changes: 4 additions & 0 deletions clix/src/main/kotlin/so/clix/services/SessionService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ internal class SessionService(
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

fun stop() {
ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
}

override fun onStart(owner: LifecycleOwner) {
checkOrStartSession()
}
Expand Down
27 changes: 27 additions & 0 deletions clix/src/test/kotlin/so/clix/core/ClixTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import org.robolectric.annotation.Config
import so.clix.services.DeviceService
import so.clix.services.EventService
import so.clix.services.NotificationService
import so.clix.services.SessionService
import so.clix.services.StorageService
import so.clix.services.TokenService
import so.clix.utils.logging.ClixLogLevel
import so.clix.utils.logging.ClixLogger
Expand All @@ -35,6 +37,8 @@ class ClixTest {
private lateinit var eventService: EventService
private lateinit var tokenService: TokenService
private lateinit var notificationService: NotificationService
private lateinit var sessionService: SessionService
private lateinit var storageService: StorageService

private lateinit var notificationManager: NotificationManagerCompat

Expand All @@ -46,6 +50,8 @@ class ClixTest {
eventService = mockk(relaxed = true)
tokenService = mockk(relaxed = true)
notificationService = mockk(relaxed = true)
sessionService = mockk(relaxed = true)
storageService = mockk(relaxed = true)
notificationManager = mockk(relaxed = true)

// Mock application context
Expand Down Expand Up @@ -74,6 +80,12 @@ class ClixTest {
every { Clix.eventService } returns eventService
every { Clix.tokenService } returns tokenService
every { Clix.notificationService } returns notificationService
every { Clix.sessionService } returns sessionService
every { Clix.storageService } returns storageService

// Directly assign nullable field (mockkObject only intercepts the getter,
// but Kotlin may access the backing field directly for non-lateinit vars)
Clix.sessionService = sessionService

// Create a mock environment
val mockEnvironment = mockk<ClixEnvironment>()
Expand Down Expand Up @@ -117,6 +129,21 @@ class ClixTest {
coVerify { deviceService.setProjectUserId(userId) }
}

@Test
fun `it should reset all local state`() {
// Given
initializeAndInjectMocks()

// When
Clix.reset()

// Then
verify { notificationService.reset() }
verify { sessionService.stop() }
verify { storageService.remove("clix_device_id") }
verify { storageService.remove("clix_session_last_activity") }
}

@Test
fun `it should set user property`() = runBlocking {
// Given
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
clix = "1.4.1"
clix = "1.5.0"
# Plugins
android-gradle-plugin = "8.9.2"
gms = "4.4.2"
Expand Down