Skip to content

Commit 3104655

Browse files
committed
First draft of RibLifecycle
1 parent 27d8eba commit 3104655

File tree

17 files changed

+640
-81
lines changed

17 files changed

+640
-81
lines changed

android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ tasks.withType<KotlinCompile>().configureEach {
7070
freeCompilerArgs.addAll(
7171
"-Xexplicit-api=warning",
7272
"-Xjvm-default=enable",
73-
"-opt-in=kotlin.RequiresOptIn",
7473
)
7574
}
7675
}

android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
@file:Suppress("invisible_reference", "invisible_member")
17+
@file:OptIn(InternalRibsApi::class)
1718

1819
package com.uber.rib.core
1920

@@ -24,7 +25,6 @@ import android.view.ViewGroup
2425
import androidx.annotation.CallSuper
2526
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction
2627
import com.uber.autodispose.lifecycle.LifecycleEndedException
27-
import com.uber.autodispose.lifecycle.LifecycleNotStartedException
2828
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
2929
import com.uber.rib.core.lifecycle.ActivityCallbackEvent
3030
import com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.create
@@ -37,6 +37,11 @@ import com.uber.rib.core.lifecycle.ActivityCallbackEvent.Companion.createWindowF
3737
import com.uber.rib.core.lifecycle.ActivityLifecycleEvent
3838
import com.uber.rib.core.lifecycle.ActivityLifecycleEvent.Companion.create
3939
import com.uber.rib.core.lifecycle.ActivityLifecycleEvent.Companion.createOnCreateEvent
40+
import com.uber.rib.core.lifecycle.RibLifecycle
41+
import com.uber.rib.core.lifecycle.RibLifecycleOwner
42+
import com.uber.rib.core.lifecycle.internal.InternalRibLifecycle
43+
import com.uber.rib.core.lifecycle.internal.actualRibLifecycle
44+
import com.uber.rib.core.lifecycle.internal.asScopeCompletable
4045
import io.reactivex.CompletableSource
4146
import io.reactivex.Observable
4247
import kotlinx.coroutines.channels.BufferOverflow
@@ -48,19 +53,27 @@ import kotlinx.coroutines.rx2.asObservable
4853
abstract class RibActivity :
4954
CoreAppCompatActivity(),
5055
ActivityStarter,
56+
RibLifecycleOwner<ActivityLifecycleEvent>,
5157
LifecycleScopeProvider<ActivityLifecycleEvent>,
5258
RxActivityEvents {
5359
private var router: ViewRouter<*, *>? = null
5460

55-
private val _lifecycleFlow =
56-
MutableSharedFlow<ActivityLifecycleEvent>(1, 0, BufferOverflow.DROP_OLDEST)
61+
private val _ribLifecycle = InternalRibLifecycle(LIFECYCLE_RANGE)
62+
override val ribLifecycle: RibLifecycle<ActivityLifecycleEvent>
63+
get() = _ribLifecycle
5764

58-
open val lifecycleFlow: SharedFlow<ActivityLifecycleEvent>
59-
get() = _lifecycleFlow
65+
@Volatile private var mockedRibLifecycleRef: RibLifecycle<ActivityLifecycleEvent>? = null
66+
67+
@Deprecated("This field should never be used on real code", level = DeprecationLevel.ERROR)
68+
final override val actualRibLifecycle: RibLifecycle<ActivityLifecycleEvent>
69+
get() = actualRibLifecycle(::mockedRibLifecycleRef, LIFECYCLE_RANGE)
6070

6171
@Volatile private var _lifecycleObservable: Observable<ActivityLifecycleEvent>? = null
72+
73+
@Suppress("DEPRECATION_ERROR")
6274
private val lifecycleObservable
63-
get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() }
75+
get() =
76+
::_lifecycleObservable.setIfNullAndGet { actualRibLifecycle.lifecycleFlow.asObservable() }
6477

6578
private val _callbacksFlow =
6679
MutableSharedFlow<ActivityCallbackEvent>(0, 1, BufferOverflow.DROP_OLDEST)
@@ -84,14 +97,14 @@ abstract class RibActivity :
8497
lifecycleFlow.replayCache.lastOrNull()
8598

8699
final override fun requestScope(): CompletableSource =
87-
lifecycleFlow.asScopeCompletable(lifecycleRange)
100+
lifecycleFlow.asScopeCompletable(LIFECYCLE_RANGE)
88101

89102
@Initializer
90103
@CallSuper
91104
override fun onCreate(savedInstanceState: android.os.Bundle?) {
92105
super.onCreate(savedInstanceState)
93106
val rootViewGroup = findViewById<ViewGroup>(android.R.id.content)
94-
_lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState))
107+
_ribLifecycle.lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState))
95108
val wrappedBundle: Bundle? =
96109
if (savedInstanceState != null) Bundle(savedInstanceState) else null
97110
router = createRouter(rootViewGroup)
@@ -113,13 +126,13 @@ abstract class RibActivity :
113126
@CallSuper
114127
override fun onStart() {
115128
super.onStart()
116-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.START))
129+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.START))
117130
}
118131

119132
@CallSuper
120133
override fun onResume() {
121134
super.onResume()
122-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.RESUME))
135+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.RESUME))
123136
}
124137

125138
@CallSuper
@@ -136,19 +149,19 @@ abstract class RibActivity :
136149

137150
@CallSuper
138151
override fun onPause() {
139-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.PAUSE))
152+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.PAUSE))
140153
super.onPause()
141154
}
142155

143156
@CallSuper
144157
override fun onStop() {
145-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.STOP))
158+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.STOP))
146159
super.onStop()
147160
}
148161

149162
@CallSuper
150163
override fun onDestroy() {
151-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.DESTROY))
164+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.DESTROY))
152165
router?.let {
153166
it.dispatchDetach()
154167
RibEvents.getInstance().emitEvent(RibEventType.DETACHED, it, null)
@@ -196,7 +209,7 @@ abstract class RibActivity :
196209
}
197210

198211
override fun onUserLeaveHint() {
199-
_lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.USER_LEAVING))
212+
_ribLifecycle.lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.USER_LEAVING))
200213
super.onUserLeaveHint()
201214
}
202215

@@ -251,12 +264,8 @@ abstract class RibActivity :
251264
)
252265
}
253266
}
254-
}
255-
}
256267

257-
private val <T : Comparable<T>> LifecycleScopeProvider<T>.lifecycleRange: ClosedRange<T>
258-
get() {
259-
val lastEmittedEvent = peekLifecycle() ?: throw LifecycleNotStartedException()
260-
val finishingEvent = correspondingEvents().apply(lastEmittedEvent)
261-
return lastEmittedEvent..finishingEvent
268+
private val LIFECYCLE_RANGE =
269+
createOnCreateEvent(null)..create(ActivityLifecycleEvent.Type.DESTROY)
262270
}
271+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) 2023. Uber Technologies
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@file:Suppress("MatchingDeclarationName", "ktlint:filename")
17+
18+
package com.uber.rib.core
19+
20+
@Retention(value = AnnotationRetention.BINARY)
21+
@Target(
22+
AnnotationTarget.CLASS,
23+
AnnotationTarget.FUNCTION,
24+
AnnotationTarget.TYPEALIAS,
25+
AnnotationTarget.PROPERTY,
26+
)
27+
@RequiresOptIn(
28+
level = RequiresOptIn.Level.ERROR,
29+
message = "This is an internal RIBs API that should not be used by the public.",
30+
)
31+
public annotation class InternalRibsApi

android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@ import androidx.annotation.VisibleForTesting
2020
import com.uber.autodispose.lifecycle.CorrespondingEventsFunction
2121
import com.uber.autodispose.lifecycle.LifecycleEndedException
2222
import com.uber.rib.core.lifecycle.InteractorEvent
23+
import com.uber.rib.core.lifecycle.RibLifecycle
24+
import com.uber.rib.core.lifecycle.coroutineScope as lifecycleCoroutineScope
25+
import com.uber.rib.core.lifecycle.internal.InternalRibLifecycle
26+
import com.uber.rib.core.lifecycle.internal.actualRibLifecycle
27+
import com.uber.rib.core.lifecycle.internal.asScopeCompletable
2328
import io.reactivex.CompletableSource
2429
import io.reactivex.Observable
2530
import javax.inject.Inject
2631
import kotlin.properties.ReadWriteProperty
2732
import kotlin.reflect.KProperty
28-
import kotlinx.coroutines.channels.BufferOverflow
29-
import kotlinx.coroutines.flow.MutableSharedFlow
30-
import kotlinx.coroutines.flow.SharedFlow
33+
import kotlinx.coroutines.CoroutineScope
3134
import kotlinx.coroutines.rx2.asObservable
3235

3336
/**
@@ -36,17 +39,11 @@ import kotlinx.coroutines.rx2.asObservable
3639
* @param <P> the type of [Presenter].
3740
* @param <R> the type of [Router].
3841
*/
42+
@OptIn(InternalRibsApi::class)
3943
public abstract class Interactor<P : Any, R : Router<*>>() : InteractorType {
44+
4045
@Inject public lateinit var injectedPresenter: P
4146
internal var actualPresenter: P? = null
42-
private val _lifecycleFlow = MutableSharedFlow<InteractorEvent>(1, 0, BufferOverflow.DROP_OLDEST)
43-
public open val lifecycleFlow: SharedFlow<InteractorEvent>
44-
get() = _lifecycleFlow
45-
46-
@Volatile private var _lifecycleObservable: Observable<InteractorEvent>? = null
47-
private val lifecycleObservable
48-
get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() }
49-
5047
private val routerDelegate = InitOnceProperty<R>()
5148

5249
/** @return the router for this interactor. */
@@ -57,22 +54,43 @@ public abstract class Interactor<P : Any, R : Router<*>>() : InteractorType {
5754
this.actualPresenter = presenter
5855
}
5956

57+
private val _ribLifecycle = InternalRibLifecycle(lifecycleRange)
58+
override val ribLifecycle: RibLifecycle<InteractorEvent>
59+
get() = _ribLifecycle
60+
61+
// For retro compatibility
62+
63+
@Volatile private var mockedRibLifecycleRef: RibLifecycle<InteractorEvent>? = null
64+
65+
@Deprecated("This field should never be used on real code", level = DeprecationLevel.ERROR)
66+
final override val actualRibLifecycle: RibLifecycle<InteractorEvent>
67+
get() = actualRibLifecycle(::mockedRibLifecycleRef, lifecycleRange)
68+
69+
@Volatile private var _lifecycleObservable: Observable<InteractorEvent>? = null
70+
71+
@Suppress("DEPRECATION_ERROR")
72+
private val lifecycleObservable
73+
get() =
74+
::_lifecycleObservable.setIfNullAndGet { actualRibLifecycle.lifecycleFlow.asObservable() }
75+
6076
// ---- LifecycleScopeProvider overrides ---- //
6177

6278
final override fun lifecycle(): Observable<InteractorEvent> = lifecycleObservable
63-
6479
final override fun correspondingEvents(): CorrespondingEventsFunction<InteractorEvent> =
6580
LIFECYCLE_MAP_FUNCTION
6681

67-
final override fun peekLifecycle(): InteractorEvent? = lifecycleFlow.replayCache.lastOrNull()
82+
@Suppress("DEPRECATION_ERROR")
83+
final override fun peekLifecycle(): InteractorEvent? =
84+
actualRibLifecycle.lifecycleFlow.replayCache.lastOrNull()
6885

86+
@Suppress("DEPRECATION_ERROR")
6987
final override fun requestScope(): CompletableSource =
70-
lifecycleFlow.asScopeCompletable(lifecycleRange)
88+
actualRibLifecycle.lifecycleFlow.asScopeCompletable(lifecycleRange)
7189

7290
// ---- InteractorType overrides ---- //
7391

7492
override fun isAttached(): Boolean =
75-
_lifecycleFlow.replayCache.lastOrNull() == InteractorEvent.ACTIVE
93+
ribLifecycle.lifecycleFlow.replayCache.lastOrNull() == InteractorEvent.ACTIVE
7694

7795
override fun handleBackPress(): Boolean = false
7896

@@ -101,15 +119,15 @@ public abstract class Interactor<P : Any, R : Router<*>>() : InteractorType {
101119
protected open fun onSaveInstanceState(outState: Bundle) {}
102120

103121
public open fun dispatchAttach(savedInstanceState: Bundle?) {
104-
_lifecycleFlow.tryEmit(InteractorEvent.ACTIVE)
122+
_ribLifecycle.lifecycleFlow.tryEmit(InteractorEvent.ACTIVE)
105123
(getPresenter() as? Presenter)?.dispatchLoad()
106124
didBecomeActive(savedInstanceState)
107125
}
108126

109127
public open fun dispatchDetach(): P {
110128
(getPresenter() as? Presenter)?.dispatchUnload()
111129
willResignActive()
112-
_lifecycleFlow.tryEmit(InteractorEvent.INACTIVE)
130+
_ribLifecycle.lifecycleFlow.tryEmit(InteractorEvent.INACTIVE)
113131
return getPresenter()
114132
}
115133

@@ -161,8 +179,7 @@ public abstract class Interactor<P : Any, R : Router<*>>() : InteractorType {
161179
}
162180

163181
public companion object {
164-
@get:JvmSynthetic internal val lifecycleRange = InteractorEvent.ACTIVE..InteractorEvent.INACTIVE
165-
182+
private val lifecycleRange = InteractorEvent.ACTIVE..InteractorEvent.INACTIVE
166183
private val LIFECYCLE_MAP_FUNCTION =
167184
CorrespondingEventsFunction { interactorEvent: InteractorEvent ->
168185
when (interactorEvent) {
@@ -172,3 +189,9 @@ public abstract class Interactor<P : Any, R : Router<*>>() : InteractorType {
172189
}
173190
}
174191
}
192+
193+
@Deprecated(
194+
"Replace the 'com.uber.core.coroutineScope' import with 'com.uber.core.lifecycle.coroutineScope'",
195+
)
196+
public val Interactor<*, *>.coroutineScope: CoroutineScope
197+
get() = this.lifecycleCoroutineScope

android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/InteractorType.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ package com.uber.rib.core
1717

1818
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
1919
import com.uber.rib.core.lifecycle.InteractorEvent
20+
import com.uber.rib.core.lifecycle.RibLifecycleOwner
2021

2122
/**
2223
* An interface used as the upper bound of the generic used by [Router]s to avoid cyclic generic
2324
* types
2425
*/
25-
public interface InteractorType : LifecycleScopeProvider<InteractorEvent> {
26+
public interface InteractorType :
27+
RibLifecycleOwner<InteractorEvent>, LifecycleScopeProvider<InteractorEvent> {
2628
/** @return `true` if the controller is attached, `false` if not. */
2729
public fun isAttached(): Boolean
2830

0 commit comments

Comments
 (0)