@@ -18,6 +18,12 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
18
18
import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
19
19
import androidx.lifecycle.HasDefaultViewModelProviderFactory
20
20
import androidx.lifecycle.Lifecycle
21
+ import androidx.lifecycle.Lifecycle.Event.ON_CREATE
22
+ import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
23
+ import androidx.lifecycle.Lifecycle.Event.ON_PAUSE
24
+ import androidx.lifecycle.Lifecycle.Event.ON_RESUME
25
+ import androidx.lifecycle.Lifecycle.Event.ON_START
26
+ import androidx.lifecycle.Lifecycle.Event.ON_STOP
21
27
import androidx.lifecycle.LifecycleEventObserver
22
28
import androidx.lifecycle.LifecycleObserver
23
29
import androidx.lifecycle.LifecycleOwner
@@ -99,34 +105,14 @@ class ModoScreenAndroidAdapter private constructor(
99
105
enableSavedStateHandles()
100
106
}
101
107
102
- private fun onCreate (savedState : Bundle ? ) {
103
- check(! isCreated) { " onCreate already called" }
104
- isCreated = true
105
- controller.performRestore(savedState)
106
- initEvents.forEach {
107
- lifecycle.safeHandleLifecycleEvent(it)
108
- }
109
- }
110
-
111
- private fun emitOnStartEvents () {
112
- startEvents.forEach {
113
- lifecycle.safeHandleLifecycleEvent(it)
114
- }
115
- }
116
-
117
- private fun emitOnStopEvents () {
118
- stopEvents.forEach {
119
- lifecycle.safeHandleLifecycleEvent(it)
120
- }
121
- }
122
-
123
108
@Composable
124
109
fun ProvideAndroidIntegration (
125
- content : @Composable () -> Unit
110
+ manualResumePause : Boolean = false,
111
+ content : @Composable () -> Unit ,
126
112
) {
127
113
val context: Context = LocalContext .current
128
114
val parentLifecycleOwner = LocalLifecycleOwner .current
129
- LifecycleDisposableEffect (context, parentLifecycleOwner) {
115
+ LifecycleDisposableEffect (context, parentLifecycleOwner, manualResumePause ) {
130
116
@Suppress(" SpreadOperator" )
131
117
CompositionLocalProvider (* getProviders()) {
132
118
content()
@@ -141,18 +127,31 @@ class ModoScreenAndroidAdapter private constructor(
141
127
*/
142
128
override fun onPreDispose () {
143
129
// Log.d("LifecycleDebug", "${screen.screenKey} ModoScreenAndroidAdapter.onPreDispose, emit ON_DESTROY event.")
144
- disposeEvents.forEach { event ->
145
- lifecycle.safeHandleLifecycleEvent(event)
146
- }
130
+ safeHandleLifecycleEvent(ON_DESTROY )
131
+ }
132
+
133
+ override fun onPause () {
134
+ safeHandleLifecycleEvent(ON_PAUSE )
135
+ }
136
+
137
+ override fun onResume () {
138
+ safeHandleLifecycleEvent(ON_RESUME )
147
139
}
148
140
141
+ override fun toString (): String = " ${ModoScreenAndroidAdapter ::class .simpleName} , screenKey: ${screen.screenKey} "
142
+
149
143
@Suppress(" UnusedParameter" )
150
144
private fun onDispose () {
151
145
// Log.d("LifecycleDebug", "${screen.screenKey} ModoScreenAndroidAdapter.onDispose. Clear ViewModelStore.")
152
146
viewModelStore.clear()
153
147
}
154
148
155
- override fun toString (): String = " ${ModoScreenAndroidAdapter ::class .simpleName} , screenKey: ${screen.screenKey} "
149
+ private fun onCreate (savedState : Bundle ? ) {
150
+ check(! isCreated) { " onCreate already called" }
151
+ isCreated = true
152
+ controller.performRestore(savedState)
153
+ safeHandleLifecycleEvent(ON_CREATE )
154
+ }
156
155
157
156
private fun performSave (outState : Bundle ) {
158
157
controller.performSave(outState)
@@ -209,6 +208,7 @@ class ModoScreenAndroidAdapter private constructor(
209
208
private fun LifecycleDisposableEffect (
210
209
context : Context ,
211
210
parentLifecycleOwner : LifecycleOwner ,
211
+ manualResumePause : Boolean ,
212
212
content : @Composable () -> Unit
213
213
) {
214
214
val activity = remember(context) {
@@ -220,34 +220,31 @@ class ModoScreenAndroidAdapter private constructor(
220
220
}
221
221
222
222
DisposableEffect (this ) {
223
- emitOnStartEvents()
223
+ safeHandleLifecycleEvent(ON_START )
224
+ if (! manualResumePause) {
225
+ safeHandleLifecycleEvent(ON_RESUME )
226
+ }
224
227
onDispose { }
225
228
}
226
229
227
230
content()
228
231
229
232
DisposableEffect (this ) {
230
233
val unregisterLifecycle = registerParentLifecycleListener(parentLifecycleOwner) {
231
- LifecycleEventObserver { owner, event ->
232
- when {
233
- /* *
234
- * Instance of the screen isn't recreated during config changes so skip this event
235
- * to avoid crash while accessing to ViewModel with SavedStateHandle, because after
236
- * ON_DESTROY, [androidx.lifecycle.SavedStateHandleController] is marked as not
237
- * attached and next call of [registerSavedStateProvider] after recreating Activity
238
- * on the same instance causing the crash.
239
- *
240
- * Also when activity is destroyed, but not finished, screen is not destroyed.
241
- *
242
- * In the case of Fragments, we unsubscribe before ON_DESTROY event, so there is no problem with this.
243
- */
244
- event == Lifecycle .Event .ON_DESTROY && (activity?.isFinishing == false || activity?.isChangingConfigurations == true ) ->
245
- return @LifecycleEventObserver
246
- // when the Application goes to background, perform save
247
- event == Lifecycle .Event .ON_STOP ->
248
- performSave(savedState)
234
+ LifecycleEventObserver { _, event ->
235
+ // when the Application goes to background, perform save
236
+ if (event == ON_STOP ) {
237
+ performSave(savedState)
238
+ }
239
+ if (
240
+ needPropagateLifecycleEventFromParent(
241
+ event,
242
+ isActivityFinishing = activity?.isFinishing,
243
+ isChangingConfigurations = activity?.isChangingConfigurations
244
+ )
245
+ ) {
246
+ safeHandleLifecycleEvent(event)
249
247
}
250
- lifecycle.safeHandleLifecycleEvent(event)
251
248
}
252
249
}
253
250
@@ -257,53 +254,77 @@ class ModoScreenAndroidAdapter private constructor(
257
254
// when the screen goes to stack, perform save
258
255
performSave(savedState)
259
256
// notify lifecycle screen listeners
260
- emitOnStopEvents()
257
+ if (! manualResumePause) {
258
+ safeHandleLifecycleEvent(ON_PAUSE )
259
+ }
260
+ safeHandleLifecycleEvent(ON_STOP )
261
261
}
262
262
}
263
263
}
264
264
265
- private fun LifecycleRegistry.safeHandleLifecycleEvent (event : Lifecycle .Event ) {
266
- val currentState = currentState
267
- val skippEvent = ! currentState.isAtLeast(Lifecycle .State .INITIALIZED ) ||
268
- // Protection from double event sending from the parent
269
- ((event in startEvents || event in initEvents) && event.targetState <= currentState) ||
270
- (event in stopEvents && event.targetState >= currentState)
271
-
272
- // For debugging
273
- // Log.d("ModoScreenAndroidAdapter", "safeHandleLifecycleEvent ${screen.screenKey} $event")
265
+ private fun safeHandleLifecycleEvent (event : Lifecycle .Event ) {
266
+ val skippEvent = needSkipEvent(lifecycle.currentState, event)
274
267
if (! skippEvent) {
275
- handleLifecycleEvent(event)
268
+ // Log.d("ModoScreenAndroidAdapter", "${screen.screenKey} handleLifecycleEvent $event")
269
+ lifecycle.handleLifecycleEvent(event)
276
270
}
277
271
}
278
272
279
273
companion object {
280
274
281
- private val initEvents = arrayOf(
282
- Lifecycle .Event .ON_CREATE
275
+ private val moveLifecycleStateUpEvents = setOf (
276
+ ON_CREATE ,
277
+ ON_START ,
278
+ ON_RESUME
283
279
)
284
280
285
- private val startEvents = arrayOf(
286
- Lifecycle .Event .ON_START ,
287
- Lifecycle .Event .ON_RESUME
288
- )
289
-
290
- private val stopEvents = arrayOf(
291
- Lifecycle .Event .ON_PAUSE ,
292
- Lifecycle .Event .ON_STOP
293
- )
294
-
295
- private val disposeEvents = arrayOf(
296
- Lifecycle .Event .ON_DESTROY
281
+ private val moveLifecycleStateDownEvents = setOf (
282
+ ON_STOP ,
283
+ ON_PAUSE ,
284
+ ON_DESTROY
297
285
)
298
286
299
287
/* *
300
288
* Creates delegate for integration with android for the given [screen] or returns existed from cache.
301
289
*/
290
+ @JvmStatic
302
291
fun get (screen : Screen ): ModoScreenAndroidAdapter =
303
292
ScreenModelStore .getOrPutDependency(
304
293
screen = screen,
305
- name = " AndroidScreenLifecycleOwner " ,
294
+ name = LifecycleDependency . KEY ,
306
295
onDispose = { it.onDispose() },
307
296
) { ModoScreenAndroidAdapter (screen) }
297
+
298
+ @JvmStatic
299
+ fun needPropagateLifecycleEventFromParent (
300
+ event : Lifecycle .Event ,
301
+ isActivityFinishing : Boolean? ,
302
+ isChangingConfigurations : Boolean?
303
+ ) =
304
+ /*
305
+ * Instance of the screen isn't recreated during config changes so skip this event
306
+ * to avoid crash while accessing to ViewModel with SavedStateHandle, because after
307
+ * ON_DESTROY, [androidx.lifecycle.SavedStateHandleController] is marked as not
308
+ * attached and next call of [registerSavedStateProvider] after recreating Activity
309
+ * on the same instance causing the crash.
310
+ *
311
+ * Also, when activity is destroyed, but not finished, screen is not destroyed.
312
+ *
313
+ * In the case of Fragments, we unsubscribe before ON_DESTROY event, so there is no problem with this.
314
+ */
315
+ if (event == ON_DESTROY && (isActivityFinishing == false || isChangingConfigurations == true )) {
316
+ false
317
+ } else {
318
+ // Parent can only move lifecycle state down. Because parent cant be already resumed, but child is not, because of running animation.
319
+ event !in moveLifecycleStateUpEvents
320
+ }
321
+
322
+ @JvmStatic
323
+ internal fun needSkipEvent (currentState : Lifecycle .State , event : Lifecycle .Event ) =
324
+ ! currentState.isAtLeast(Lifecycle .State .INITIALIZED ) ||
325
+ // Skipping events that moves lifecycle state up, but this state is already reached.
326
+ (event in moveLifecycleStateUpEvents && event.targetState <= currentState) ||
327
+ // Skipping events that moves lifecycle state down, but this state is already reached.
328
+ (event in moveLifecycleStateDownEvents && event.targetState >= currentState)
308
329
}
309
330
}
0 commit comments