Skip to content

Commit 343c6e2

Browse files
Ian BirdIanDBird
authored andcommitted
Add support to detect when UID2Manager finishes initializing
1 parent 6b3f0b1 commit 343c6e2

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

dev-app/src/main/java/com/uid2/dev/ui/MainScreenViewModel.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope
77
import com.uid2.UID2Manager
88
import com.uid2.UID2ManagerState.Established
99
import com.uid2.UID2ManagerState.Expired
10+
import com.uid2.UID2ManagerState.Loading
1011
import com.uid2.UID2ManagerState.NoIdentity
1112
import com.uid2.UID2ManagerState.OptOut
1213
import com.uid2.UID2ManagerState.RefreshExpired
@@ -33,12 +34,12 @@ import kotlinx.coroutines.launch
3334

3435
sealed interface MainScreenAction : ViewModelAction {
3536
data class EmailChanged(val address: String) : MainScreenAction
36-
object ResetButtonPressed : MainScreenAction
37-
object RefreshButtonPressed : MainScreenAction
37+
data object ResetButtonPressed : MainScreenAction
38+
data object RefreshButtonPressed : MainScreenAction
3839
}
3940

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

6162
when (state) {
63+
is Loading -> Unit
6264
is Established -> _viewState.emit(UserUpdatedState(state.identity, ESTABLISHED))
6365
is Refreshed -> _viewState.emit(UserUpdatedState(state.identity, REFRESHED))
6466
is NoIdentity -> _viewState.emit(UserUpdatedState(null, NO_IDENTITY))

sdk/src/main/java/com/uid2/UID2Manager.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.content.Context
44
import com.uid2.UID2ManagerState.Established
55
import com.uid2.UID2ManagerState.Expired
66
import com.uid2.UID2ManagerState.Invalid
7+
import com.uid2.UID2ManagerState.Loading
78
import com.uid2.UID2ManagerState.NoIdentity
89
import com.uid2.UID2ManagerState.OptOut
910
import com.uid2.UID2ManagerState.RefreshExpired
@@ -58,6 +59,7 @@ public interface UID2ManagerIdentityChangedListener {
5859
* A interface defining the flow of state communicated by the [UID2Manager].
5960
*/
6061
public sealed interface UID2ManagerState {
62+
public data object Loading : UID2ManagerState
6163
public data class Established(val identity: UID2Identity) : UID2ManagerState
6264
public data class Refreshed(val identity: UID2Identity) : UID2ManagerState
6365
public data object NoIdentity : UID2ManagerState
@@ -93,7 +95,23 @@ public class UID2Manager internal constructor(
9395
*/
9496
public var onIdentityChangedListener: UID2ManagerIdentityChangedListener? = null
9597

96-
private val _state = MutableStateFlow<UID2ManagerState>(NoIdentity)
98+
/**
99+
* Gets or sets a listener which can be used to determine if the [UID2Manager] instance has finished initializing.
100+
* Initializing includes any time required to restore a previously persisted [UID2Identity] from storage.
101+
*
102+
* If this property is set *after* initialization is complete, the callback will be invoked immediately.
103+
*/
104+
public var onInitialized: (() -> Unit)? = null
105+
set(value) {
106+
field = value
107+
108+
// If we've already finished initializing, we should immediately invoke the callback.
109+
if (initialized.isCompleted) {
110+
value?.invoke()
111+
}
112+
}
113+
114+
private val _state = MutableStateFlow<UID2ManagerState>(Loading)
97115

98116
/**
99117
* The flow representing the state of the UID2Manager.
@@ -135,6 +153,7 @@ public class UID2Manager internal constructor(
135153
*/
136154
public val currentIdentityStatus: IdentityStatus
137155
get() = when (_state.value) {
156+
is Loading -> NO_IDENTITY // Not available yet.
138157
is Established -> ESTABLISHED
139158
is Refreshed -> REFRESHED
140159
is NoIdentity -> NO_IDENTITY
@@ -164,6 +183,9 @@ public class UID2Manager internal constructor(
164183

165184
validateAndSetIdentity(it.first, it.second, false)
166185
}
186+
187+
// If we have a callback provided, invoke it.
188+
onInitialized?.invoke()
167189
}
168190
}
169191

sdk/src/test/java/com/uid2/UID2ManagerTest.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,28 @@ class UID2ManagerTest {
6767
manager = withManager(client, storageManager, timeUtils, testDispatcher, false, listener)
6868
}
6969

70+
@Test
71+
fun `reports when initialization is complete`() = runTest(testDispatcher) {
72+
var isInitialized = false
73+
val onInitialized = { isInitialized = true }
74+
75+
val manager = UID2Manager(client, storageManager, timeUtils, testDispatcher, false, logger).apply {
76+
this.checkExpiration = false
77+
this.onInitialized = onInitialized
78+
}
79+
80+
// Verify that the manager invokes our callback after it's been able to load the identity from storage.
81+
assertFalse(isInitialized)
82+
testDispatcher.scheduler.advanceUntilIdle()
83+
assertTrue(isInitialized)
84+
85+
// Reset our state and re-assign the manager's callback. Verify that even though initialization is complete, our
86+
// callback is invoked immediately.
87+
isInitialized = false
88+
manager.onInitialized = onInitialized
89+
assertTrue(isInitialized)
90+
}
91+
7092
@Test
7193
fun `restores identity from storage`() = runTest(testDispatcher) {
7294
// Verify that the initial state of the manager reflects the restored Identity.
@@ -75,6 +97,19 @@ class UID2ManagerTest {
7597
assertEquals(initialStatus, manager.currentIdentityStatus)
7698
}
7799

100+
@Test
101+
fun `set identity immediately available`() = runTest(testDispatcher) {
102+
val identity = withRandomIdentity()
103+
104+
// By default, the Manager will have restored the previously persisted Identity. Let's reset our state so this
105+
// will be a new Identity.
106+
manager.resetIdentity()
107+
108+
// Verify that immediately after setting an identity, it's immediately available via currentIdentity.
109+
manager.setIdentity(identity)
110+
assertEquals(identity, manager.currentIdentity)
111+
}
112+
78113
@Test
79114
fun `updates identity when set`() = runTest(testDispatcher) {
80115
val identity = withRandomIdentity()

0 commit comments

Comments
 (0)