From adc3b0df53c4fa73f7daca432e427fdefe732ab6 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Thu, 21 Sep 2023 15:33:13 -0300 Subject: [PATCH] Get rid of suppressions for "invisible_reference" and "invisible_member" For accessing internal declarations from other modules (e.g. access `rib-base` internals from `rib-test`), we are currently suppressing compiler errors with `@file:Suppress("invisible_reference", "invisible_member")` A better approach is to: 1. Create an `internal` opt-in annotation. 2. Make the "accessible to friend modules" component `public` 3. Mark the (now public) component with the opt-in annotation. 4. Opt-in to the annotation from `build.gradle`. Because the new annotation is `internal`, it cannot be normally accessed from external modules. But Gradle can see it if it's part of the source set. This makes the internal visibility of those friend-modules APIs even stricter, since consumers now cannot just suppress `"invisible_reference"` and `"invisible_member"` to directly access the friend-module API. Plus, we get rid of the hacky suppressions in our codebase. --- .../{build.gradle => build.gradle.kts} | 12 +++++++++- .../kotlin/com/uber/rib/core/RibActivity.kt | 12 ++++------ .../kotlin/com/uber/rib/core/FlowAsScope.kt | 4 +++- .../kotlin/com/uber/rib/core/Interactor.kt | 13 ++++++++-- .../com/uber/rib/core/LazyBackingProperty.kt | 6 +++-- .../kotlin/com/uber/rib/core/Presenter.kt | 4 ++++ .../main/kotlin/com/uber/rib/core/Router.kt | 12 ++++++++-- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 3 +++ .../com/uber/rib/core/WorkerScopeProvider.kt | 4 +++- .../rib/core/internal/CoreFriendModuleApi.kt | 23 ++++++++++++++++++ .../uber/rib/core/LazyBackingPropertyTest.kt | 5 ++++ .../uber/rib/core/WorkerScopeProviderTest.kt | 2 ++ .../{build.gradle => build.gradle.kts} | 13 ++++++++-- .../uber/rib/core/TestRibCoroutineScopes.kt | 2 -- .../com/uber/rib/core/RibCoroutineScopes.kt | 14 +++++++---- .../internal/CoroutinesFriendModuleApi.kt | 24 +++++++++++++++++++ .../{build.gradle => build.gradle.kts} | 7 ++++++ .../com/uber/rib/core/RouterDebugUtils.kt | 2 -- .../{build.gradle => build.gradle.kts} | 10 ++++++++ .../com/uber/rib/core/FakeInteractor.kt | 2 -- .../com/uber/rib/core/InteractorHelper.kt | 2 -- .../kotlin/com/uber/rib/core/WorkerHelper.kt | 2 -- .../{build.gradle => build.gradle.kts} | 16 ++++++++++--- .../com/uber/rib/workflow/core/StepTester.kt | 12 ++++------ .../kotlin/com/uber/rib/workflow/core/Step.kt | 19 +++++++++++---- .../core/internal/WorkflowFriendModuleApi.kt | 23 ++++++++++++++++++ .../com/uber/rib/workflow/core/StepTest.kt | 2 ++ 27 files changed, 202 insertions(+), 48 deletions(-) rename android/libraries/rib-android/{build.gradle => build.gradle.kts} (86%) create mode 100644 android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/internal/CoreFriendModuleApi.kt rename android/libraries/rib-coroutines-test/{build.gradle => build.gradle.kts} (84%) create mode 100644 android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/internal/CoroutinesFriendModuleApi.kt rename android/libraries/rib-debug-utils/{build.gradle => build.gradle.kts} (82%) rename android/libraries/rib-test/{build.gradle => build.gradle.kts} (83%) rename android/libraries/rib-workflow-test/{build.gradle => build.gradle.kts} (78%) create mode 100644 android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/internal/WorkflowFriendModuleApi.kt diff --git a/android/libraries/rib-android/build.gradle b/android/libraries/rib-android/build.gradle.kts similarity index 86% rename from android/libraries/rib-android/build.gradle rename to android/libraries/rib-android/build.gradle.kts index 17f04c47..547fc123 100644 --- a/android/libraries/rib-android/build.gradle +++ b/android/libraries/rib-android/build.gradle.kts @@ -19,7 +19,17 @@ plugins { } android { - namespace "com.uber.rib.android" + namespace = "com.uber.rib.android" +} + +kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("com.uber.rib.core.internal.CoreFriendModuleApi") + } + } + } } dependencies { diff --git a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt index 04400d73..cdec97e1 100644 --- a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt +++ b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RibActivity.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core import android.content.Intent @@ -45,7 +43,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.rx2.asObservable /** Base implementation for all VIP [android.app.Activity]s. */ -abstract class RibActivity : +public abstract class RibActivity : CoreAppCompatActivity(), ActivityStarter, LifecycleScopeProvider, @@ -55,7 +53,7 @@ abstract class RibActivity : private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) - open val lifecycleFlow: SharedFlow + public open val lifecycleFlow: SharedFlow get() = _lifecycleFlow @Volatile private var _lifecycleObservable: Observable? = null @@ -64,7 +62,7 @@ abstract class RibActivity : private val _callbacksFlow = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) - open val callbacksFlow: SharedFlow + public open val callbacksFlow: SharedFlow get() = _callbacksFlow @Volatile private var _callbacksObservable: Observable? = null @@ -215,7 +213,7 @@ abstract class RibActivity : * @return the [Interactor] when the activity has alive. * @throws IllegalStateException if the activity has not been created or has been destroyed. */ - open val interactor: Interactor<*, *> + public open val interactor: Interactor<*, *> get() = if (router != null) { router?.interactor as Interactor<*, *> @@ -232,7 +230,7 @@ abstract class RibActivity : */ protected abstract fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *> - companion object { + public companion object { /** * Figures out which corresponding next lifecycle event in which to unsubscribe, for Activities. */ diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/FlowAsScope.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/FlowAsScope.kt index 546a6ebe..b60925ab 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/FlowAsScope.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/FlowAsScope.kt @@ -19,6 +19,7 @@ package com.uber.rib.core import com.uber.autodispose.lifecycle.LifecycleEndedException import com.uber.autodispose.lifecycle.LifecycleNotStartedException +import com.uber.rib.core.internal.CoreFriendModuleApi import io.reactivex.CompletableSource import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -36,7 +37,8 @@ import kotlinx.coroutines.rx2.rxCompletable * 2. [LifecycleEndedException], if the last emitted event is in the end (inclusive) or beyond * [range]. */ -internal fun > SharedFlow.asScopeCompletable( +@CoreFriendModuleApi +public fun > SharedFlow.asScopeCompletable( range: ClosedRange, context: CoroutineContext = EmptyCoroutineContext, ): CompletableSource { diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt index a705fbd2..4301bae4 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Interactor.kt @@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents +import com.uber.rib.core.internal.CoreFriendModuleApi import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -39,12 +40,15 @@ import kotlinx.coroutines.rx2.asObservable */ public abstract class Interactor

>() : InteractorType, RibActionEmitter { @Inject public lateinit var injectedPresenter: P - internal var actualPresenter: P? = null + + @CoreFriendModuleApi public var actualPresenter: P? = null private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) public open val lifecycleFlow: SharedFlow get() = _lifecycleFlow @Volatile private var _lifecycleObservable: Observable? = null + + @OptIn(CoreFriendModuleApi::class) private val lifecycleObservable get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() } @@ -54,6 +58,7 @@ public abstract class Interactor

>() : InteractorType, Rib public open var router: R by routerDelegate protected set + @OptIn(CoreFriendModuleApi::class) protected constructor(presenter: P) : this() { this.actualPresenter = presenter } @@ -67,6 +72,7 @@ public abstract class Interactor

>() : InteractorType, Rib final override fun peekLifecycle(): InteractorEvent? = lifecycleFlow.replayCache.lastOrNull() + @OptIn(CoreFriendModuleApi::class) final override fun requestScope(): CompletableSource = lifecycleFlow.asScopeCompletable(lifecycleRange) @@ -149,13 +155,15 @@ public abstract class Interactor

>() : InteractorType, Rib return getPresenter() } - internal fun setRouterInternal(router: Router<*>) { + @CoreFriendModuleApi + public fun setRouterInternal(router: Router<*>) { if (routerDelegate != null) { this.router = router as R } } /** @return the currently attached presenter if there is one */ + @OptIn(CoreFriendModuleApi::class) @VisibleForTesting private fun getPresenter(): P { val presenter: P? = @@ -172,6 +180,7 @@ public abstract class Interactor

>() : InteractorType, Rib return presenter } + @OptIn(CoreFriendModuleApi::class) @VisibleForTesting internal fun setPresenter(presenter: P) { actualPresenter = presenter diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/LazyBackingProperty.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/LazyBackingProperty.kt index 141a0779..5da67a8f 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/LazyBackingProperty.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/LazyBackingProperty.kt @@ -15,6 +15,7 @@ */ package com.uber.rib.core +import com.uber.rib.core.internal.CoreFriendModuleApi import kotlin.reflect.KMutableProperty0 /** @@ -30,8 +31,9 @@ import kotlin.reflect.KMutableProperty0 * * To properly support concurrency, the backing mutable property should be [Volatile]. */ -internal inline fun KMutableProperty0.setIfNullAndGet( +@CoreFriendModuleApi +public inline fun KMutableProperty0.setIfNullAndGet( initializer: () -> T, ): T = get() ?: synchronized(Lock) { get() ?: initializer().also { set(it) } } -private object Lock +@CoreFriendModuleApi public object Lock diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Presenter.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Presenter.kt index d1f74821..da804916 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Presenter.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Presenter.kt @@ -17,6 +17,7 @@ package com.uber.rib.core import androidx.annotation.CallSuper import com.uber.autodispose.ScopeProvider +import com.uber.rib.core.internal.CoreFriendModuleApi import com.uber.rib.core.lifecycle.PresenterEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -39,6 +40,8 @@ public abstract class Presenter : ScopeProvider, RibActionEmitter { get() = _lifecycleFlow @Volatile private var _lifecycleObservable: Observable? = null + + @OptIn(CoreFriendModuleApi::class) private val lifecycleObservable get() = ::_lifecycleObservable.setIfNullAndGet { lifecycleFlow.asObservable() } @@ -70,6 +73,7 @@ public abstract class Presenter : ScopeProvider, RibActionEmitter { /** @return an observable of this controller's lifecycle events. */ public fun lifecycle(): Observable = lifecycleObservable + @OptIn(CoreFriendModuleApi::class) final override fun requestScope(): CompletableSource = lifecycleFlow.asScopeCompletable(lifecycleRange) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Router.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Router.kt index 0e381bbc..372c4d50 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Router.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Router.kt @@ -20,6 +20,7 @@ import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents +import com.uber.rib.core.internal.CoreFriendModuleApi import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList @@ -40,7 +41,8 @@ protected constructor( get() = interactor as Interactor<*, *> /** @return the Tag. */ - internal var tag: String? = null + @CoreFriendModuleApi + public var tag: String? = null private set private var savedInstanceState: Bundle? = null private var isLoaded = false @@ -58,6 +60,7 @@ protected constructor( (component as? InteractorBaseComponent>)?.inject(interactorGeneric) } + @OptIn(CoreFriendModuleApi::class) protected open fun attachToInteractor() { interactorGeneric.setRouterInternal(this) } @@ -99,6 +102,7 @@ protected constructor( * @param childRouter the [Router] to be attached. * @param tag an identifier to namespace saved instance state [Bundle] objects. */ + @OptIn(CoreFriendModuleApi::class) @MainThread public open fun attachChild(childRouter: Router<*>, tag: String) { for (child in children) { @@ -146,6 +150,7 @@ protected constructor( * * @param childRouter the [Router] to be detached. */ + @OptIn(CoreFriendModuleApi::class) @MainThread public open fun detachChild(childRouter: Router<*>) { val isChildRemoved = children.remove(childRouter) @@ -185,6 +190,7 @@ protected constructor( dispatchAttach(savedInstanceState, javaClass.name) } + @OptIn(CoreFriendModuleApi::class) @CallSuper @Initializer public open fun dispatchAttach(savedInstanceState: Bundle?, tag: String) { @@ -218,7 +224,8 @@ protected constructor( * * @return Children. */ - internal open fun getChildren(): List> { + @CoreFriendModuleApi + public open fun getChildren(): List> { return children } @@ -226,6 +233,7 @@ protected constructor( saveInstanceState(outState) } + @OptIn(CoreFriendModuleApi::class) protected open fun saveInstanceState(outState: Bundle) { val interactorSavedInstanceState = Bundle() interactorGeneric.onSaveInstanceStateInternal(interactorSavedInstanceState) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerBinder.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerBinder.kt index d63ae194..7373c3de 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerBinder.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerBinder.kt @@ -19,6 +19,7 @@ import androidx.annotation.VisibleForTesting import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents +import com.uber.rib.core.internal.CoreFriendModuleApi import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent @@ -157,6 +158,7 @@ public object WorkerBinder { } } + @OptIn(CoreFriendModuleApi::class) @JvmStatic @VisibleForTesting @Deprecated( @@ -226,6 +228,7 @@ public object WorkerBinder { * @param workerLifecycle the worker lifecycle event provider * @param worker the class that wants to be informed when to start and stop doing work */ + @OptIn(CoreFriendModuleApi::class) @JvmStatic @Deprecated( message = diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerScopeProvider.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerScopeProvider.kt index 6da9a07f..e22f8c98 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerScopeProvider.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerScopeProvider.kt @@ -16,6 +16,7 @@ package com.uber.rib.core import com.uber.autodispose.ScopeProvider +import com.uber.rib.core.internal.CoreFriendModuleApi import com.uber.rib.core.lifecycle.WorkerEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -23,7 +24,8 @@ import io.reactivex.Observable /** [ScopeProvider] for [Worker] instances. */ public open class WorkerScopeProvider internal constructor(delegate: ScopeProvider) : ScopeProvider by delegate { - internal constructor(lifecycle: Observable) : this(lifecycle.asScopeProvider()) + @CoreFriendModuleApi + public constructor(lifecycle: Observable) : this(lifecycle.asScopeProvider()) } private fun Observable<*>.asScopeProvider() = asCompletableSource().asScopeProvider() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/internal/CoreFriendModuleApi.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/internal/CoreFriendModuleApi.kt new file mode 100644 index 00000000..8bf24fe3 --- /dev/null +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/internal/CoreFriendModuleApi.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uber.rib.core.internal + +/* + * Methods that are visible only to rib-base friend modules. + * + * Anything marked with this annotation is not intended for public use. + */ +@RequiresOptIn(level = RequiresOptIn.Level.ERROR) internal annotation class CoreFriendModuleApi diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt index 5ce17201..d0846d36 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt @@ -16,6 +16,7 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat +import com.uber.rib.core.internal.CoreFriendModuleApi import io.reactivex.Observable import kotlinx.coroutines.channels.produce import kotlinx.coroutines.channels.toList @@ -26,6 +27,8 @@ import org.mockito.kotlin.mock class LazyBackingPropertyTest { @Volatile private var _expensiveObject: ExpensiveObject? = null + + @OptIn(CoreFriendModuleApi::class) private val expensiveObject get() = ::_expensiveObject.setIfNullAndGet { ExpensiveObject() } @@ -47,6 +50,8 @@ class LazyBackingPropertyTest { private open class ClassToBeMocked { @Volatile private var _prop: Observable? = null + + @OptIn(CoreFriendModuleApi::class) val prop: Observable get() = ::_prop.setIfNullAndGet { Observable.just(1, 2, 3) } } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerScopeProviderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerScopeProviderTest.kt index 94943273..682349a1 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerScopeProviderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerScopeProviderTest.kt @@ -17,6 +17,7 @@ package com.uber.rib.core import com.jakewharton.rxrelay2.BehaviorRelay import com.jakewharton.rxrelay2.Relay +import com.uber.rib.core.internal.CoreFriendModuleApi import com.uber.rib.core.lifecycle.WorkerEvent import io.reactivex.observers.TestObserver import org.junit.Before @@ -27,6 +28,7 @@ class WorkerScopeProviderTest { private val lifecycle: Relay = BehaviorRelay.create() private lateinit var testObserver: TestObserver + @OptIn(CoreFriendModuleApi::class) @Before fun setUp() { testObserver = TestObserver() diff --git a/android/libraries/rib-coroutines-test/build.gradle b/android/libraries/rib-coroutines-test/build.gradle.kts similarity index 84% rename from android/libraries/rib-coroutines-test/build.gradle rename to android/libraries/rib-coroutines-test/build.gradle.kts index ae4e3b01..fae9d484 100644 --- a/android/libraries/rib-coroutines-test/build.gradle +++ b/android/libraries/rib-coroutines-test/build.gradle.kts @@ -19,8 +19,18 @@ plugins { alias(libs.plugins.mavenPublish) } +kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("com.uber.rib.core.internal.CoroutinesFriendModuleApi") + } + } + } +} + dependencies { - api(project(':libraries:rib-coroutines')) + api(project(":libraries:rib-coroutines")) api(testLibs.coroutines.test) api(testLibs.junit) @@ -36,5 +46,4 @@ dependencies { testImplementation(libs.coroutines.android) testImplementation(libs.annotation) testImplementation(libs.android.api) - } diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt index 237be997..c0b79485 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core import android.app.Application diff --git a/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutineScopes.kt b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutineScopes.kt index e01a4fc7..6d67979d 100755 --- a/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutineScopes.kt +++ b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutineScopes.kt @@ -18,6 +18,7 @@ package com.uber.rib.core import android.app.Application import com.uber.autodispose.ScopeProvider import com.uber.autodispose.coroutinesinterop.asCoroutineScope +import com.uber.rib.core.internal.CoroutinesFriendModuleApi import java.util.WeakHashMap import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -34,6 +35,7 @@ import kotlinx.coroutines.job * This scope is bound to * [RibDispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] */ +@OptIn(CoroutinesFriendModuleApi::class) public val ScopeProvider.coroutineScope: CoroutineScope by LazyCoroutineScope { val context: CoroutineContext = @@ -52,6 +54,7 @@ public val ScopeProvider.coroutineScope: CoroutineScope by * This scope is bound to * [RibDispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] */ +@OptIn(CoroutinesFriendModuleApi::class) public val Application.coroutineScope: CoroutineScope by LazyCoroutineScope { val context: CoroutineContext = @@ -63,17 +66,18 @@ public val Application.coroutineScope: CoroutineScope by CoroutineScope(context) } -internal class LazyCoroutineScope(val initializer: This.() -> CoroutineScope) { - companion object { +@CoroutinesFriendModuleApi +public class LazyCoroutineScope(private val initializer: This.() -> CoroutineScope) { + public companion object { private val values = WeakHashMap() // Used to get and set Test overrides from rib-coroutines-test utils - operator fun get(provider: Any) = values[provider] - operator fun set(provider: Any, scope: CoroutineScope?) { + public operator fun get(provider: Any): CoroutineScope? = values[provider] + public operator fun set(provider: Any, scope: CoroutineScope?) { values[provider] = scope } } - operator fun getValue(thisRef: This, property: KProperty<*>): CoroutineScope = + public operator fun getValue(thisRef: This, property: KProperty<*>): CoroutineScope = synchronized(LazyCoroutineScope) { return values.getOrPut(thisRef) { thisRef.initializer().apply { diff --git a/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/internal/CoroutinesFriendModuleApi.kt b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/internal/CoroutinesFriendModuleApi.kt new file mode 100644 index 00000000..c6396235 --- /dev/null +++ b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/internal/CoroutinesFriendModuleApi.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uber.rib.core.internal + +/* + * Methods that are visible only to rib-coroutines friend modules. + * + * Anything marked with this annotation is not intended for public use. + */ +@RequiresOptIn(level = RequiresOptIn.Level.ERROR) +internal annotation class CoroutinesFriendModuleApi diff --git a/android/libraries/rib-debug-utils/build.gradle b/android/libraries/rib-debug-utils/build.gradle.kts similarity index 82% rename from android/libraries/rib-debug-utils/build.gradle rename to android/libraries/rib-debug-utils/build.gradle.kts index c8593593..1f17e5e4 100644 --- a/android/libraries/rib-debug-utils/build.gradle +++ b/android/libraries/rib-debug-utils/build.gradle.kts @@ -20,6 +20,13 @@ plugins { } kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("com.uber.rib.core.internal.CoreFriendModuleApi") + } + } + } explicitApi() jvmToolchain(8) } diff --git a/android/libraries/rib-debug-utils/src/main/kotlin/com/uber/rib/core/RouterDebugUtils.kt b/android/libraries/rib-debug-utils/src/main/kotlin/com/uber/rib/core/RouterDebugUtils.kt index 25eaf2f7..68a56695 100644 --- a/android/libraries/rib-debug-utils/src/main/kotlin/com/uber/rib/core/RouterDebugUtils.kt +++ b/android/libraries/rib-debug-utils/src/main/kotlin/com/uber/rib/core/RouterDebugUtils.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core /** Debugging utilities when working with Routers. */ diff --git a/android/libraries/rib-test/build.gradle b/android/libraries/rib-test/build.gradle.kts similarity index 83% rename from android/libraries/rib-test/build.gradle rename to android/libraries/rib-test/build.gradle.kts index 2b1cf898..a8c98d45 100644 --- a/android/libraries/rib-test/build.gradle +++ b/android/libraries/rib-test/build.gradle.kts @@ -19,6 +19,16 @@ plugins { alias(libs.plugins.mavenPublish) } +kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("com.uber.rib.core.internal.CoreFriendModuleApi") + } + } + } +} + dependencies { api(project(":libraries:rib-base")) implementation(libs.rxjava2) diff --git a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeInteractor.kt b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeInteractor.kt index 120d7c1c..e1688f70 100644 --- a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeInteractor.kt +++ b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/FakeInteractor.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core @SuppressWarnings("RibInteractorOnIteractor") diff --git a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/InteractorHelper.kt b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/InteractorHelper.kt index 1de7ebf3..27571402 100644 --- a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/InteractorHelper.kt +++ b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/InteractorHelper.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core import org.mockito.AdditionalMatchers.or diff --git a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/WorkerHelper.kt b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/WorkerHelper.kt index efcc5959..5c54ea98 100644 --- a/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/WorkerHelper.kt +++ b/android/libraries/rib-test/src/main/kotlin/com/uber/rib/core/WorkerHelper.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.core import com.uber.rib.core.lifecycle.WorkerEvent diff --git a/android/libraries/rib-workflow-test/build.gradle b/android/libraries/rib-workflow-test/build.gradle.kts similarity index 78% rename from android/libraries/rib-workflow-test/build.gradle rename to android/libraries/rib-workflow-test/build.gradle.kts index 0e2aebb8..312d5de7 100644 --- a/android/libraries/rib-workflow-test/build.gradle +++ b/android/libraries/rib-workflow-test/build.gradle.kts @@ -19,13 +19,23 @@ plugins { } android { - namespace "com.uber.rib.workflow.test" + namespace = "com.uber.rib.workflow.test" // This module is only testing utilities. Given this code isn't intended to be run inside a production // android app this module confuses android lint. Let's just disable lint errors here. lint { - abortOnError false - disable 'InvalidPackage' + abortOnError = false + disable.add("InvalidPackage") + } +} + +kotlin { + sourceSets { + configureEach { + languageSettings { + optIn("com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi") + } + } } } diff --git a/android/libraries/rib-workflow-test/src/main/kotlin/com/uber/rib/workflow/core/StepTester.kt b/android/libraries/rib-workflow-test/src/main/kotlin/com/uber/rib/workflow/core/StepTester.kt index fb7310c2..20f4f138 100644 --- a/android/libraries/rib-workflow-test/src/main/kotlin/com/uber/rib/workflow/core/StepTester.kt +++ b/android/libraries/rib-workflow-test/src/main/kotlin/com/uber/rib/workflow/core/StepTester.kt @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:Suppress("invisible_reference", "invisible_member") - package com.uber.rib.workflow.core import com.google.common.base.Optional @@ -23,7 +21,7 @@ import io.reactivex.Observable import io.reactivex.observers.TestObserver /** Utility to expose [Observable] instances on a [Step] for unit testing purposes. */ -object StepTester { +public object StepTester { /** * Exposes a [Step] instances observable for testing purposes. * @@ -33,7 +31,7 @@ object StepTester { * @return a [Observable] that runs the steps action. */ @JvmStatic - fun exposeObservable( + public fun exposeObservable( step: Step, ): Observable>> { return step.asObservable() @@ -48,7 +46,7 @@ object StepTester { * @return the data of the step */ @JvmStatic - fun exposeStepData(step: Step.Data): T? { + public fun exposeStepData(step: Step.Data): T? { return step.getValue() } @@ -60,7 +58,7 @@ object StepTester { * @param type of next actionable item for a step. */ @JvmStatic - fun assertStepNotYetEmitted( + public fun assertStepNotYetEmitted( testSubscriber: TestObserver>>, ) { testSubscriber.run { @@ -78,7 +76,7 @@ object StepTester { * @param type of next actionable item for a step. */ @JvmStatic - fun assertStepEmitted( + public fun assertStepEmitted( testSubscriber: TestObserver>>, ) { testSubscriber.assertValueCount(1) diff --git a/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Step.kt b/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Step.kt index 912bad8b..e3a46096 100644 --- a/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Step.kt +++ b/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/Step.kt @@ -17,6 +17,7 @@ package com.uber.rib.workflow.core import com.google.common.base.Optional import com.uber.rib.core.lifecycle.InteractorEvent +import com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers @@ -44,8 +45,11 @@ private constructor( * @param the actionable item type returned by the next step. * @return a [Step] to chain more calls to. */ + @OptIn(WorkflowFriendModuleApi::class) @SuppressWarnings("RxJavaToSingle") // Replace singleOrError() with firstOrError() - open fun onStep(func: BiFunction>): Step { + public open fun onStep( + func: BiFunction>, + ): Step { return Step( asObservable() .flatMap { data: Optional> -> @@ -59,11 +63,13 @@ private constructor( ) } + @OptIn(WorkflowFriendModuleApi::class) internal open fun asResultObservable(): Observable> { return asObservable().map { data -> Optional.fromNullable(data.orNull()?.getValue()) } } - internal open fun asObservable(): Observable>> { + @WorkflowFriendModuleApi + public open fun asObservable(): Observable>> { val cachedObservable: Observable>> = stepDataSingle.toObservable().observeOn(AndroidSchedulers.mainThread()).cache() return cachedObservable.flatMap { dataOptional: Optional> -> @@ -88,9 +94,12 @@ private constructor( * @param value for this instance. * @param actionableItem for this instance. */ - open class Data(private val value: T, internal val actionableItem: A) { + public open class Data( + private val value: T, + internal val actionableItem: A, + ) { - internal open fun getValue() = value + @WorkflowFriendModuleApi public open fun getValue() = value companion object { /** @@ -102,7 +111,7 @@ private constructor( * @return a new [Step.Data] instance. */ @JvmStatic - fun toActionableItem(actionableItem: A): Data { + public fun toActionableItem(actionableItem: A): Data { return Data(NoValueHolder.INSTANCE, actionableItem) } } diff --git a/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/internal/WorkflowFriendModuleApi.kt b/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/internal/WorkflowFriendModuleApi.kt new file mode 100644 index 00000000..4d55f66a --- /dev/null +++ b/android/libraries/rib-workflow/src/main/kotlin/com/uber/rib/workflow/core/internal/WorkflowFriendModuleApi.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uber.rib.workflow.core.internal + +/* + * Methods that are visible only to rib-workflow friend modules. + * + * Anything marked with this annotation is not intended for public use. + */ +@RequiresOptIn(level = RequiresOptIn.Level.ERROR) internal annotation class WorkflowFriendModuleApi diff --git a/android/libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/StepTest.kt b/android/libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/StepTest.kt index 781871a0..aac73984 100644 --- a/android/libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/StepTest.kt +++ b/android/libraries/rib-workflow/src/test/kotlin/com/uber/rib/workflow/core/StepTest.kt @@ -19,6 +19,7 @@ import com.google.common.base.Optional import com.google.common.truth.Truth.assertThat import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.workflow.core.Step.Data +import com.uber.rib.workflow.core.internal.WorkflowFriendModuleApi import io.reactivex.Observable import io.reactivex.observers.TestObserver import io.reactivex.subjects.BehaviorSubject @@ -27,6 +28,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test +@OptIn(WorkflowFriendModuleApi::class) class StepTest { @get:Rule var androidSchedulersRuleRx2 = AndroidSchedulersRule()