Skip to content
Open
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
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ volley = "1.2.1"
gson = "2.8.9"
work_manager = "2.8.1"
androidx_lifecycle = "2.8.7"
androidx_startup = "1.2.0"
androidx_core_ktx = "1.13.0"
androidx_annotations = "1.3.0"
constraint_layout = "2.1.4"
Expand Down Expand Up @@ -86,6 +87,7 @@ room_ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room_compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
work_manager = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work_manager" }
androidx_lifecycle = { group = "androidx.lifecycle", name = "lifecycle-process", version.ref = "androidx_lifecycle" }
androidx_startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidx_startup" }
hms_push = { group = "com.huawei.hms", name = "push", version.ref = "hms_push" }
hms_ads_identifier = { group = "com.huawei.hms", name = "ads-identifier", version.ref = "hms_ads_identifier" }
constraint_layout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraint_layout" }
Expand Down
1 change: 1 addition & 0 deletions sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ dependencies {

// Handle app lifecycle
implementation libs.androidx.lifecycle
implementation libs.androidx.startup
implementation libs.threetenabp

// Glide
Expand Down
10 changes: 10 additions & 0 deletions sdk/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@

<uses-sdk tools:overrideLibrary="io.mockk, io.mockk.proxy.android" />
<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="cloud.mindbox.mobile_sdk.managers.MindboxLifecycleInitializer"
android:value="androidx.startup" />
</provider>

<activity
android:name="cloud.mindbox.mobile_sdk.inapp.presentation.actions.PushActivationActivity"
android:excludeFromRecents="true"
Expand Down
272 changes: 143 additions & 129 deletions sdk/src/main/java/cloud/mindbox/mobile_sdk/Mindbox.kt
Original file line number Diff line number Diff line change
Expand Up @@ -554,152 +554,166 @@ public object Mindbox : MindboxLog {
context: Context,
configuration: MindboxConfiguration,
pushServices: List<MindboxPushService>,
) {
LoggingExceptionHandler.runCatching {
verifyThreadExecution(methodName = "init")
val currentProcessName = context.getCurrentProcessName()
if (!context.isMainProcess(currentProcessName)) {
logW("Skip Mindbox init not in main process! Current process $currentProcessName")
return@runCatching
}
Stopwatch.start(Stopwatch.INIT_SDK)
): Unit = loggingRunCatching {
verifyThreadExecution(methodName = "init")
val currentProcessName = context.getCurrentProcessName()
if (!context.isMainProcess(currentProcessName)) {
logW("Skip Mindbox init not in main process! Current process $currentProcessName")
return@loggingRunCatching
}
Stopwatch.start(Stopwatch.INIT_SDK)

initComponents(context.applicationContext)
pushConverters = selectPushServiceHandler(pushServices)
logI("init in $currentProcessName. firstInitCall: ${firstInitCall.get()}, " +
initComponents(context.applicationContext)
pushConverters = selectPushServiceHandler(pushServices)
logI(
"init in $currentProcessName. firstInitCall: ${firstInitCall.get()}, " +
"configuration: $configuration, pushServices: " +
pushServices.joinToString(", ") { it.javaClass.simpleName } +
", SdkVersion:${getSdkVersion()}, CommonSdkVersion:${MindboxCommon.VERSION_NAME}")
", SdkVersion:${getSdkVersion()}, CommonSdkVersion:${MindboxCommon.VERSION_NAME}",
)

if (!firstInitCall.get()) {
InitializeLock.reset(InitializeLock.State.SAVE_MINDBOX_CONFIG)
} else {
userVisitManager.saveUserVisit()
if (!firstInitCall.get()) {
InitializeLock.reset(InitializeLock.State.SAVE_MINDBOX_CONFIG)
} else {
userVisitManager.saveUserVisit()
}

launchInitJob(context, configuration, pushServices)
setupLifecycleManager(context)
attachLifecycleCallbacks()
}

private fun launchInitJob(
context: Context,
configuration: MindboxConfiguration,
pushServices: List<MindboxPushService>,
) {
initScope.launch {
InitializeLock.await(InitializeLock.State.MIGRATION)
val checkResult = checkConfig(configuration)
val validatedConfiguration = validateConfiguration(configuration)
DbManager.saveConfigurations(Configuration(configuration))
logI("init. checkResult: $checkResult")
if (checkResult != ConfigUpdate.NOT_UPDATED && !MindboxPreferences.isFirstInitialize) {
logI("init. softReinitialization")
softReinitialization(context.applicationContext)
}

initScope.launch {
InitializeLock.await(InitializeLock.State.MIGRATION)
val checkResult = checkConfig(configuration)
val validatedConfiguration = validateConfiguration(configuration)
DbManager.saveConfigurations(Configuration(configuration))
logI("init. checkResult: $checkResult")
if (checkResult != ConfigUpdate.NOT_UPDATED && !MindboxPreferences.isFirstInitialize) {
logI("init. softReinitialization")
softReinitialization(context.applicationContext)
}
if (checkResult == ConfigUpdate.UPDATED) {
setPushServiceHandler(context, pushServices)
firstInitialization(context.applicationContext, validatedConfiguration)

if (checkResult == ConfigUpdate.UPDATED) {
val isTrackVisitNotSent = Mindbox::lifecycleManager.isInitialized &&
!lifecycleManager.isTrackVisitSent()
if (isTrackVisitNotSent) {
MindboxLoggerImpl.d(this, "Track visit event with source $DIRECT")
sendTrackVisitEvent(context.applicationContext, DIRECT)
}
} else {
mindboxScope.launch {
setPushServiceHandler(context, pushServices)
firstInitialization(context.applicationContext, validatedConfiguration)

val isTrackVisitNotSent = Mindbox::lifecycleManager.isInitialized &&
!lifecycleManager.isTrackVisitSent()
if (isTrackVisitNotSent) {
MindboxLoggerImpl.d(this, "Track visit event with source $DIRECT")
sendTrackVisitEvent(context.applicationContext, DIRECT)
}
} else {
mindboxScope.launch {
setPushServiceHandler(context, pushServices)
}
MindboxEventManager.sendEventsIfExist(context.applicationContext)
}
MindboxPreferences.uuidDebugEnabled = configuration.uuidDebugEnabled
}.initState(InitializeLock.State.SAVE_MINDBOX_CONFIG)
.invokeOnCompletion { throwable ->
if (throwable == null) {
if (firstInitCall.get()) {
val activity = context as? Activity
if (activity != null && lifecycleManager.isCurrentActivityResumed) {
inAppMessageManager.registerCurrentActivity(activity)
mindboxScope.launch {
inAppMutex.withLock {
logI("Start inapp manager after init. firstInitCall: ${firstInitCall.get()}")
if (!firstInitCall.getAndSet(false)) return@launch
inAppMessageManager.listenEventAndInApp()
inAppMessageManager.initLogs()
MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
inAppMessageManager.requestConfig().join()
}
}
MindboxEventManager.sendEventsIfExist(context.applicationContext)
}
MindboxPreferences.uuidDebugEnabled = configuration.uuidDebugEnabled
}.initState(InitializeLock.State.SAVE_MINDBOX_CONFIG)
.invokeOnCompletion { throwable ->
if (throwable == null && firstInitCall.get()) {
val activity = context as? Activity
if (activity != null && lifecycleManager.isCurrentActivityResumed) {
inAppMessageManager.registerCurrentActivity(activity)
mindboxScope.launch {
inAppMutex.withLock {
logI("Start inapp manager after init. firstInitCall: ${firstInitCall.get()}")
if (!firstInitCall.getAndSet(false)) return@launch
inAppMessageManager.listenEventAndInApp()
inAppMessageManager.initLogs()
MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
inAppMessageManager.requestConfig().join()
}
}
}
}
// Handle back app in foreground
(context.applicationContext as? Application)?.apply {
val applicationLifecycle = ProcessLifecycleOwner.get().lifecycle
}
}

if (!Mindbox::lifecycleManager.isInitialized) {
val activity = context as? Activity
val isApplicationResumed = applicationLifecycle.currentState == RESUMED
if (isApplicationResumed && activity == null) {
logE("Incorrect context type for calling init in this place")
}
if (isApplicationResumed || context !is Application) {
logW(
"We recommend to call Mindbox.init() synchronously from " +
"Application.onCreate. If you can't do so, don't forget to " +
"call Mindbox.initPushServices from Application.onCreate",
)
}
private fun setupLifecycleManager(context: Context) {
if (!Mindbox::lifecycleManager.isInitialized) {
val applicationLifecycle = ProcessLifecycleOwner.get().lifecycle
val activity = context as? Activity
val isApplicationResumed = applicationLifecycle.currentState == RESUMED
if (isApplicationResumed && activity == null) {
logE("Incorrect context type for calling init in this place")
}
if (isApplicationResumed || context !is Application) {
logW(
"We recommend to call Mindbox.init() synchronously from " +
"Application.onCreate. If you can't do so, don't forget to " +
"call Mindbox.initPushServices from Application.onCreate",
)
}

logI("init. init lifecycleManager")
lifecycleManager = LifecycleManager(
currentActivityName = activity?.javaClass?.name,
currentIntent = activity?.intent,
isAppInBackground = !isApplicationResumed,
onActivityStarted = { startedActivity ->
UuidCopyManager.onAppMovedToForeground(startedActivity)
mindboxScope.launch {
if (!MindboxPreferences.isFirstInitialize) {
updateAppInfo(startedActivity.applicationContext)
}
}
},
onActivityPaused = { pausedActivity ->
inAppMessageManager.onPauseCurrentActivity(pausedActivity)
},
onActivityResumed = { resumedActivity ->
inAppMessageManager.onResumeCurrentActivity(
resumedActivity
)
if (firstInitCall.get()) {
mindboxScope.launch {
InitializeLock.await(InitializeLock.State.SAVE_MINDBOX_CONFIG)
inAppMutex.withLock {
logI("Start inapp manager after resume activity. firstInitCall: ${firstInitCall.get()}")
if (!firstInitCall.getAndSet(false)) return@launch
inAppMessageManager.listenEventAndInApp()
inAppMessageManager.initLogs()
MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
inAppMessageManager.requestConfig().join()
}
}
}
},
onActivityStopped = { resumedActivity ->
inAppMessageManager.onStopCurrentActivity(resumedActivity)
},
onTrackVisitReady = { source, requestUrl ->
sessionStorageManager.hasSessionExpired()
eventScope.launch {
sendTrackVisitEvent(
MindboxDI.appModule.appContext,
source,
requestUrl
)
}
}
)
} else {
unregisterActivityLifecycleCallbacks(lifecycleManager)
applicationLifecycle.removeObserver(lifecycleManager)
lifecycleManager.wasReinitialized()
val existingManager = LifecycleManager.instance
lifecycleManager = if (existingManager != null) {
logI("init. attaching callbacks to existing lifecycleManager")
existingManager
} else {
logI("init. creating lifecycleManager (startup initializer not found)")
LifecycleManager(
currentActivityName = activity?.javaClass?.name,
currentIntent = activity?.intent,
isAppInBackground = !isApplicationResumed,
).also { manager ->
(context.applicationContext as? Application)?.apply {
registerActivityLifecycleCallbacks(manager)
applicationLifecycle.addObserver(manager)
}
}
}
} else {
lifecycleManager.wasReinitialized()
}
}

registerActivityLifecycleCallbacks(lifecycleManager)
applicationLifecycle.addObserver(lifecycleManager)
private fun attachLifecycleCallbacks() {
lifecycleManager.onActivityStarted = { startedActivity ->
UuidCopyManager.onAppMovedToForeground(startedActivity)
mindboxScope.launch {
if (!MindboxPreferences.isFirstInitialize) {
updateAppInfo(startedActivity.applicationContext)
}
}
}
lifecycleManager.onActivityPaused = { pausedActivity ->
inAppMessageManager.onPauseCurrentActivity(pausedActivity)
}
lifecycleManager.onActivityResumed = { resumedActivity ->
inAppMessageManager.onResumeCurrentActivity(resumedActivity)
if (firstInitCall.get()) {
mindboxScope.launch {
InitializeLock.await(InitializeLock.State.SAVE_MINDBOX_CONFIG)
inAppMutex.withLock {
logI("Start in-app manager after resume activity. firstInitCall: ${firstInitCall.get()}")
if (!firstInitCall.getAndSet(false)) return@launch
inAppMessageManager.listenEventAndInApp()
inAppMessageManager.initLogs()
MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
inAppMessageManager.requestConfig().join()
}
}
}
}
lifecycleManager.onActivityStopped = { stoppedActivity ->
inAppMessageManager.onStopCurrentActivity(stoppedActivity)
}
lifecycleManager.onTrackVisitReady = { source, requestUrl ->
sessionStorageManager.hasSessionExpired()
eventScope.launch {
sendTrackVisitEvent(
MindboxDI.appModule.appContext,
source,
requestUrl,
)
}
}
}
Expand Down
Loading
Loading