Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8253a5a
feat(demo): add E2E test tags and value masking
fadi-george May 19, 2026
9023b3f
refactor(demo): rename package from sdktest to example
fadi-george May 19, 2026
9d803dc
docs(demo): update build spec for E2E parity
fadi-george May 19, 2026
fb9a950
docs(demo): replace full spec with concise build guide
fadi-george May 19, 2026
8c29f4f
refactor(demo): replace LogManager with android.util.Log
fadi-george May 19, 2026
90766b1
style(demo): add shadow to top bars, rename to "Android"
fadi-george May 19, 2026
a517d0d
feat(demo): add WITH SOUND and CLEAR ALL buttons
fadi-george May 19, 2026
9156527
style(demo): clarify add button labels
fadi-george May 19, 2026
be8d2ab
refactor(demo): align UI tokens with styles.md
fadi-george May 19, 2026
6d96c76
refactor(demo): extract DemoAppBar component
fadi-george May 19, 2026
02e9496
feat(demo): enable edge-to-edge display
fadi-george May 19, 2026
07c9539
fix(demo): force light status-bar icons
fadi-george May 19, 2026
d40c841
refactor(demo): source app ID from BuildConfig
fadi-george May 19, 2026
cb8c072
fix(demo): expose testTags as resource IDs in dialogs
fadi-george May 19, 2026
17faa91
fix(demo): set snackbar content color to white
fadi-george May 19, 2026
c9c6a10
refactor(demo): improve maskValue with bullet char
fadi-george May 19, 2026
c48c728
fix(demo): default IAM paused state to false
fadi-george May 19, 2026
10fc708
refactor(demo): shorten outcome toast message
fadi-george May 19, 2026
7512b4c
fix(demo): move testTag from Box to Text
fadi-george May 19, 2026
c729a50
refactor(demo): rename TrackEventSection to CustomEventsSection
fadi-george May 19, 2026
0d0af6e
refactor(demo): simplify event tracked toast message
fadi-george May 19, 2026
08b0616
feat(demo): add check location shared button
fadi-george May 19, 2026
b9856f7
refactor(demo): rename section title to Custom Events
fadi-george May 19, 2026
d7f2344
chore(demo): preserve em dash in masked values
fadi-george May 20, 2026
61a18dd
chore(demo): fix em dash in external ID fallback
fadi-george May 20, 2026
0f8ca94
fix(demo): cleanup observers and move requestId earlier
fadi-george May 20, 2026
37496f6
refactor(demo): deduplicate notification post logic with retry
fadi-george May 21, 2026
36d7147
refactor(demo): remove E2E_MODE masking
fadi-george May 21, 2026
6b84951
refactor(demo): improve retry backoff and error logging
fadi-george May 21, 2026
96cbddf
fix(demo): broaden transient send failure detection
fadi-george May 23, 2026
a813284
fix(demo): address PR review comments on demo app refresh
fadi-george May 24, 2026
d378e67
fix(demo): propagate CancellationException through outer catches
fadi-george May 24, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ import kotlinx.coroutines.withContext

internal class OneSignalImp(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : IOneSignal, IServiceProvider {

) : IOneSignal,
IServiceProvider {
// Reset every time the synchronized(initLock) block flips state to IN_PROGRESS so that
// a retry-after-FAILED gets a fresh latch instead of an already-completed one. Mutated only
// under initLock; reads outside that lock must local-capture before suspending on it.
Expand Down Expand Up @@ -158,24 +158,25 @@ internal class OneSignalImp(
"com.onesignal.location.LocationModule",
)
private val services: ServiceProvider =
ServiceBuilder().apply {
val modules = mutableListOf<IModule>()
modules.add(CoreModule())
modules.add(SessionModule())
modules.add(UserModule())
for (moduleClassName in listOfModules) {
try {
val moduleClass = Class.forName(moduleClassName)
val moduleInstance = moduleClass.newInstance() as IModule
modules.add(moduleInstance)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
ServiceBuilder()
.apply {
val modules = mutableListOf<IModule>()
modules.add(CoreModule())
modules.add(SessionModule())
modules.add(UserModule())
for (moduleClassName in listOfModules) {
try {
val moduleClass = Class.forName(moduleClassName)
val moduleInstance = moduleClass.newInstance() as IModule
modules.add(moduleInstance)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
}
}
for (module in modules) {
module.register(this)
}
}.build()
for (module in modules) {
module.register(this)
}
}.build()

private val featureManager: IFeatureManager by lazy { services.getService<IFeatureManager>() }
private val runtimeIoDispatcher: CoroutineDispatcher
Expand Down Expand Up @@ -251,10 +252,11 @@ internal class OneSignalImp(
// anything that happens during the rest of init. FeatureManager is wired in via a
// lazy supplier — `enabledFeatureFlags` is read per-event, so resolving the manager
// can be deferred until services have bootstrapped.
otelManager = OtelLifecycleManager(
context = context,
featureManagerProvider = { services.getService<IFeatureManager>() },
).also { it.initializeFromCachedConfig() }
otelManager =
OtelLifecycleManager(
context = context,
featureManagerProvider = { services.getService<IFeatureManager>() },
).also { it.initializeFromCachedConfig() }

PreferenceStoreFix.ensureNoObfuscatedPrefStore(context)

Expand Down Expand Up @@ -547,15 +549,20 @@ internal class OneSignalImp(
@Suppress("UseCheckOrError")
private fun requireInitForOperation(operationName: String) {
when (initState) {
InitState.NOT_STARTED ->
InitState.NOT_STARTED -> {
throw IllegalStateException("Must call 'initWithContext' before '$operationName'")
}

InitState.IN_PROGRESS -> {
warnIfBlockingOnMainThread(operationName)
waitForInit(operationName = operationName)
}
InitState.FAILED ->

InitState.FAILED -> {
throw initFailureException
?: IllegalStateException("Initialization failed before '$operationName'")
}

InitState.SUCCESS -> {}
}
}
Expand Down Expand Up @@ -656,13 +663,15 @@ internal class OneSignalImp(

when (observedState) {
InitState.NOT_STARTED -> {
val message = if (operationName != null) {
"Must call 'initWithContext' before '$operationName'"
} else {
"Must call 'initWithContext' before use"
}
val message =
if (operationName != null) {
"Must call 'initWithContext' before '$operationName'"
} else {
"Must call 'initWithContext' before use"
}
throw IllegalStateException(message)
}

InitState.IN_PROGRESS -> {
Logging.debug("Waiting for init to complete...")

Expand All @@ -680,11 +689,12 @@ internal class OneSignalImp(

// Log how long initialization took
val elapsed = System.currentTimeMillis() - startTime
val message = if (operationName != null) {
"OneSignalImp initialization completed before '$operationName' (took ${elapsed}ms)"
} else {
"OneSignalImp initialization completed (took ${elapsed}ms)"
}
val message =
if (operationName != null) {
"OneSignalImp initialization completed before '$operationName' (took ${elapsed}ms)"
} else {
"OneSignalImp initialization completed (took ${elapsed}ms)"
}
Logging.debug(message)

// Re-check state after waiting - init might have failed during the wait
Expand All @@ -693,9 +703,11 @@ internal class OneSignalImp(
}
// initState is guaranteed to be SUCCESS here - consistent state
}

InitState.FAILED -> {
throw initFailureException ?: IllegalStateException("Initialization failed. Cannot proceed.")
}

else -> {
// SUCCESS - already initialized, no need to wait
}
Expand All @@ -717,14 +729,23 @@ internal class OneSignalImp(
return waitAndReturn(getter)
}
return when (initState) {
InitState.SUCCESS -> getter()
InitState.SUCCESS -> {
getter()
}

InitState.IN_PROGRESS -> {
warnIfBlockingOnMainThread(operationName = null)
waitAndReturn(getter)
}
InitState.FAILED -> throw initFailureException
?: IllegalStateException("Initialization failed. Cannot proceed.")
InitState.NOT_STARTED -> throw IllegalStateException("Must call 'initWithContext' before use")

InitState.FAILED -> {
throw initFailureException
?: IllegalStateException("Initialization failed. Cannot proceed.")
}

InitState.NOT_STARTED -> {
throw IllegalStateException("Must call 'initWithContext' before use")
}
}
}

Expand Down
Loading
Loading