Skip to content

Commit

Permalink
Add support to detect when UID2Manager finishes initializing
Browse files Browse the repository at this point in the history
  • Loading branch information
Ian Bird authored and IanDBird committed Mar 28, 2024
1 parent 6b3f0b1 commit 343c6e2
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
8 changes: 5 additions & 3 deletions dev-app/src/main/java/com/uid2/dev/ui/MainScreenViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
import com.uid2.UID2Manager
import com.uid2.UID2ManagerState.Established
import com.uid2.UID2ManagerState.Expired
import com.uid2.UID2ManagerState.Loading
import com.uid2.UID2ManagerState.NoIdentity
import com.uid2.UID2ManagerState.OptOut
import com.uid2.UID2ManagerState.RefreshExpired
Expand All @@ -33,12 +34,12 @@ import kotlinx.coroutines.launch

sealed interface MainScreenAction : ViewModelAction {
data class EmailChanged(val address: String) : MainScreenAction
object ResetButtonPressed : MainScreenAction
object RefreshButtonPressed : MainScreenAction
data object ResetButtonPressed : MainScreenAction
data object RefreshButtonPressed : MainScreenAction
}

sealed interface MainScreenState : ViewState {
object LoadingState : MainScreenState
data object LoadingState : MainScreenState
data class UserUpdatedState(val identity: UID2Identity?, val status: IdentityStatus) : MainScreenState
data class ErrorState(val error: Throwable) : MainScreenState
}
Expand All @@ -59,6 +60,7 @@ class MainScreenViewModel(
Log.d(TAG, "State Update: $state")

when (state) {
is Loading -> Unit
is Established -> _viewState.emit(UserUpdatedState(state.identity, ESTABLISHED))
is Refreshed -> _viewState.emit(UserUpdatedState(state.identity, REFRESHED))
is NoIdentity -> _viewState.emit(UserUpdatedState(null, NO_IDENTITY))
Expand Down
24 changes: 23 additions & 1 deletion sdk/src/main/java/com/uid2/UID2Manager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import com.uid2.UID2ManagerState.Established
import com.uid2.UID2ManagerState.Expired
import com.uid2.UID2ManagerState.Invalid
import com.uid2.UID2ManagerState.Loading
import com.uid2.UID2ManagerState.NoIdentity
import com.uid2.UID2ManagerState.OptOut
import com.uid2.UID2ManagerState.RefreshExpired
Expand Down Expand Up @@ -58,6 +59,7 @@ public interface UID2ManagerIdentityChangedListener {
* A interface defining the flow of state communicated by the [UID2Manager].
*/
public sealed interface UID2ManagerState {
public data object Loading : UID2ManagerState
public data class Established(val identity: UID2Identity) : UID2ManagerState
public data class Refreshed(val identity: UID2Identity) : UID2ManagerState
public data object NoIdentity : UID2ManagerState
Expand Down Expand Up @@ -93,7 +95,23 @@ public class UID2Manager internal constructor(
*/
public var onIdentityChangedListener: UID2ManagerIdentityChangedListener? = null

private val _state = MutableStateFlow<UID2ManagerState>(NoIdentity)
/**
* Gets or sets a listener which can be used to determine if the [UID2Manager] instance has finished initializing.
* Initializing includes any time required to restore a previously persisted [UID2Identity] from storage.
*
* If this property is set *after* initialization is complete, the callback will be invoked immediately.
*/
public var onInitialized: (() -> Unit)? = null
set(value) {
field = value

// If we've already finished initializing, we should immediately invoke the callback.
if (initialized.isCompleted) {
value?.invoke()
}
}

private val _state = MutableStateFlow<UID2ManagerState>(Loading)

/**
* The flow representing the state of the UID2Manager.
Expand Down Expand Up @@ -135,6 +153,7 @@ public class UID2Manager internal constructor(
*/
public val currentIdentityStatus: IdentityStatus
get() = when (_state.value) {
is Loading -> NO_IDENTITY // Not available yet.
is Established -> ESTABLISHED
is Refreshed -> REFRESHED
is NoIdentity -> NO_IDENTITY
Expand Down Expand Up @@ -164,6 +183,9 @@ public class UID2Manager internal constructor(

validateAndSetIdentity(it.first, it.second, false)
}

// If we have a callback provided, invoke it.
onInitialized?.invoke()
}
}

Expand Down
35 changes: 35 additions & 0 deletions sdk/src/test/java/com/uid2/UID2ManagerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ class UID2ManagerTest {
manager = withManager(client, storageManager, timeUtils, testDispatcher, false, listener)
}

@Test
fun `reports when initialization is complete`() = runTest(testDispatcher) {
var isInitialized = false
val onInitialized = { isInitialized = true }

val manager = UID2Manager(client, storageManager, timeUtils, testDispatcher, false, logger).apply {
this.checkExpiration = false
this.onInitialized = onInitialized
}

// Verify that the manager invokes our callback after it's been able to load the identity from storage.
assertFalse(isInitialized)
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(isInitialized)

// Reset our state and re-assign the manager's callback. Verify that even though initialization is complete, our
// callback is invoked immediately.
isInitialized = false
manager.onInitialized = onInitialized
assertTrue(isInitialized)
}

@Test
fun `restores identity from storage`() = runTest(testDispatcher) {
// Verify that the initial state of the manager reflects the restored Identity.
Expand All @@ -75,6 +97,19 @@ class UID2ManagerTest {
assertEquals(initialStatus, manager.currentIdentityStatus)
}

@Test
fun `set identity immediately available`() = runTest(testDispatcher) {
val identity = withRandomIdentity()

// By default, the Manager will have restored the previously persisted Identity. Let's reset our state so this
// will be a new Identity.
manager.resetIdentity()

// Verify that immediately after setting an identity, it's immediately available via currentIdentity.
manager.setIdentity(identity)
assertEquals(identity, manager.currentIdentity)
}

@Test
fun `updates identity when set`() = runTest(testDispatcher) {
val identity = withRandomIdentity()
Expand Down

0 comments on commit 343c6e2

Please sign in to comment.