From b6140c5f75ad781e921e5489d34194e7a7565448 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Ricau Date: Thu, 11 May 2023 14:23:35 -0700 Subject: [PATCH 01/52] Update LeakCanary to 2.10 Also added the now required "description" parameter when watching instances. Fixes #580 --- .../java/com/uber/rib/SampleApplication.java | 17 ++++--------- android/gradle/dependencies.gradle | 2 +- .../kotlin/com/uber/rib/core/RibRefWatcher.kt | 19 ++++++++++++--- .../main/kotlin/com/uber/rib/core/Router.kt | 5 +++- .../uber/rib/core/InteractorAndRouterTest.kt | 8 +++---- .../com/uber/rib/core/RibRefWatcherTest.kt | 24 +++++++++---------- 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java b/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java index 712589905..39c25026a 100644 --- a/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java +++ b/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java @@ -16,12 +16,10 @@ package com.uber.rib; import android.app.Application; -import com.squareup.leakcanary.LeakCanary; -import com.squareup.leakcanary.RefWatcher; import com.uber.rib.core.ActivityDelegate; import com.uber.rib.core.HasActivityDelegate; import com.uber.rib.core.RibRefWatcher; -import java.util.concurrent.TimeUnit; +import leakcanary.AppWatcher; public class SampleApplication extends Application implements HasActivityDelegate { @@ -31,24 +29,17 @@ public class SampleApplication extends Application implements HasActivityDelegat public void onCreate() { activityDelegate = new SampleActivityDelegate(); super.onCreate(); - if (!LeakCanary.isInAnalyzerProcess(this)) { - // This process is dedicated to LeakCanary for heap analysis. You should not init your app in - // this process. - installLeakCanary(); - } + installLeakCanary(); } /** Install leak canary for both activities and RIBs. */ private void installLeakCanary() { - final RefWatcher refWatcher = - LeakCanary.refWatcher(this).watchDelay(2, TimeUnit.SECONDS).buildAndInstall(); - LeakCanary.install(this); RibRefWatcher.getInstance() .setReferenceWatcher( new RibRefWatcher.ReferenceWatcher() { @Override - public void watch(Object object) { - refWatcher.watch(object); + public void watch(Object object, String description) { + AppWatcher.INSTANCE.getObjectWatcher().expectWeaklyReachable(object, description); } @Override diff --git a/android/gradle/dependencies.gradle b/android/gradle/dependencies.gradle index 3c0bb1df8..50c952429 100755 --- a/android/gradle/dependencies.gradle +++ b/android/gradle/dependencies.gradle @@ -125,7 +125,7 @@ def external = [ roboelectricBase: "org.robolectric:robolectric:${versions.robolectric}", rxbinding: 'com.jakewharton.rxbinding2:rxbinding:2.0.0', rxkotlin: 'io.reactivex.rxjava2:rxkotlin:2.2.0', - leakcanaryDebug: 'com.squareup.leakcanary:leakcanary-android:1.5.4', + leakcanaryDebug: 'com.squareup.leakcanary:leakcanary-android:2.10', ] diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt index 8ed91004a..6605a1849 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt @@ -34,17 +34,30 @@ public open class RibRefWatcher { referenceWatcher = watcher } + @Deprecated( + "Add the description parameter", + replaceWith = ReplaceWith("watchDeletedObject(objectToWatch, description)"), + ) + public open fun watchDeletedObject( + objectToWatch: Any?, + ) { + watchDeletedObject(objectToWatch, "missing description") + } + /** * Watch this object to verify it has no inbound references. * * @param objectToWatch the object to watch. */ - public open fun watchDeletedObject(objectToWatch: Any?) { + public open fun watchDeletedObject( + objectToWatch: Any?, + description: String, + ) { if (objectToWatch == null) { return } if (isLeakCanaryEnabled || uLeakEnabled) { - referenceWatcher?.watch(objectToWatch) + referenceWatcher?.watch(objectToWatch, description) } } @@ -97,7 +110,7 @@ public open class RibRefWatcher { * * @param objectToWatch the object to watch. */ - public fun watch(objectToWatch: Any) + public fun watch(objectToWatch: Any, description: String) /** * Method to pipe breadcrumbs into the Breadcrumb logger. 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 37041ffc8..891a110d1 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 @@ -141,7 +141,10 @@ protected constructor( public open fun detachChild(childRouter: Router<*>) { val isChildRemoved = children.remove(childRouter) val interactor = childRouter.interactor - ribRefWatcher.watchDeletedObject(interactor) + ribRefWatcher.watchDeletedObject( + interactor, + "detached child router ${childRouter.javaClass.name}", + ) ribRefWatcher.logBreadcrumb( "DETACHED", childRouter.javaClass.simpleName, diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 4faa01c6a..b324d0dc2 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -146,13 +146,13 @@ class InteractorAndRouterTest { val childInteractor = TestInteractorB() val childRouter = TestRouterB(childInteractor, component) router.attachChild(childRouter) - verify(ribRefWatcher, never()).watchDeletedObject(any()) + verify(ribRefWatcher, never()).watchDeletedObject(any(), "") // Action: Detach the child interactor. router.detachChild(childRouter) // Verify: the reference watcher observes the detached interactor and child. - verify(ribRefWatcher).watchDeletedObject(eq(childInteractor)) + verify(ribRefWatcher).watchDeletedObject(eq(childInteractor), "") } @Test @@ -166,13 +166,13 @@ class InteractorAndRouterTest { } val rootRouter = TestRouterB(component, TestInteractorB(), ribRefWatcher) val child = addTwoNestedChildInteractors() - verify(ribRefWatcher, never()).watchDeletedObject(any()) + verify(ribRefWatcher, never()).watchDeletedObject(any(), "") // Action: Detach all child interactors. rootRouter.detachChild(child) // Verify: called four times. Twice for each interactor. - verify(ribRefWatcher, times(2)).watchDeletedObject(any()) + verify(ribRefWatcher, times(2)).watchDeletedObject(any(), "") } private fun addTwoNestedChildInteractors(): Router { diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt index b3c58fffc..fb4d8c70c 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt @@ -36,14 +36,14 @@ class RibRefWatcherTest { fun watchDeletedObject_whenObjectIsNull_shouldDoNothing() { ribRefWatcher.enableLeakCanary() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(null) + ribRefWatcher.watchDeletedObject(null, "") verifyNoInteractions(referenceWatcher) } @Test fun watchDeletedObject_whenReferenceWatcherIsNull_shouldDoNothing() { ribRefWatcher.enableLeakCanary() - ribRefWatcher.watchDeletedObject(Any()) + ribRefWatcher.watchDeletedObject(Any(), "") verifyNoInteractions(referenceWatcher) } @@ -52,30 +52,30 @@ class RibRefWatcherTest { ribRefWatcher.enableLeakCanary() val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher).watch(obj, "") } @Test fun watchDeletedObject_whenNonNullRefWithDisabledLeakCanary_shouldDoNothing() { val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher, never()).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher, never()).watch(obj, "") } @Test fun watchDeletedObject_whenObjectIsNullWithULeak_shouldDoNothing() { ribRefWatcher.enableULeakLifecycleTracking() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(null) + ribRefWatcher.watchDeletedObject(null, "") verifyNoInteractions(referenceWatcher) } @Test fun watchDeletedObject_whenReferenceWatcherIsNullULeakEnabled_shouldDoNothing() { ribRefWatcher.enableULeakLifecycleTracking() - ribRefWatcher.watchDeletedObject(Any()) + ribRefWatcher.watchDeletedObject(Any(), "") verifyNoInteractions(referenceWatcher) } @@ -84,15 +84,15 @@ class RibRefWatcherTest { ribRefWatcher.enableULeakLifecycleTracking() val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher).watch(obj, "") } @Test fun watchDeletedObject_whenNonNullRefULeakDisabled_shouldDoNothing() { val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher, never()).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher, never()).watch(obj, "") } } From b0e415526b86bec1963953dc1ae5b24cd85d9dd8 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Mon, 15 May 2023 22:02:52 -0300 Subject: [PATCH 02/52] Add KDoc on `ActivityLifecycleEvent` explaining ordering semantics. Also, a couple of minor fixes: - A small warning on `RibActivity` against importing `android.R`. - Replaces deprecated `String.toLowerCase()` and `String.capitalize()` with the equivalent `String.lowercase()` and `String.replaceFirstChar(Char::titlecase)`. --- .../kotlin/com/uber/rib/core/RibActivity.kt | 3 +- .../core/lifecycle/ActivityLifecycleEvent.kt | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) 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 3a0e3f7b8..493144c04 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 @@ -17,7 +17,6 @@ package com.uber.rib.core -import android.R import android.content.Intent import android.content.res.Configuration import android.os.Build @@ -91,7 +90,7 @@ abstract class RibActivity : @CallSuper override fun onCreate(savedInstanceState: android.os.Bundle?) { super.onCreate(savedInstanceState) - val rootViewGroup = findViewById(R.id.content) + val rootViewGroup = findViewById(android.R.id.content) _lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState)) val wrappedBundle: Bundle? = if (savedInstanceState != null) Bundle(savedInstanceState) else null diff --git a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityLifecycleEvent.kt b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityLifecycleEvent.kt index a89e4223f..c1cd2cf38 100644 --- a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityLifecycleEvent.kt +++ b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/lifecycle/ActivityLifecycleEvent.kt @@ -17,7 +17,34 @@ package com.uber.rib.core.lifecycle import android.os.Bundle -/** Lifecycle events that can be emitted by Activities. */ +/** + * Lifecycle events that can be emitted by Activities. + * + * ### Ordering semantics + * + * This class implements [Comparable], but it does **not** override `equals`, so even though the + * following holds: + * ``` + * val a = createOnCreateEvent(null) + * val b = createOnCreateEvent(Bundle()) + * assertThat(a <= b).isTrue() + * assertThat(b <= a).isTrue() + * ``` + * + * The equality does not hold: + * ``` + * assertThat(a == b).isFalse() + * ``` + * + * This happens because events of type [CREATE][ActivityLifecycleEvent.Type.CREATE] hold a [Bundle], + * and even though any two `CREATE` events are equal in ordering ([compareTo]), they are never equal + * in [equals] comparison. + * + * In mathematical terms, the activity events set form a + * [total preorder](https://en.wikipedia.org/wiki/Weak_ordering#Total_preorders), but *not* a + * [total order](https://en.wikipedia.org/wiki/Total_order): it is reflexive, transitive, strongly + * connected, but **not** antisymmetric. + */ open class ActivityLifecycleEvent private constructor( /** @return this event's type. */ @@ -37,7 +64,10 @@ private constructor( override fun compareTo(other: ActivityLifecycleEvent): Int = type.compareTo(other.type) - /** An [ActivityLifecycleEvent] that encapsulates information from [Activity.onCreate]. */ + /** + * An [ActivityLifecycleEvent] that encapsulates information from + * [Activity.onCreate][android.app.Activity.onCreate]. + */ open class Create( /** @return this event's savedInstanceState data. */ open val savedInstanceState: Bundle?, @@ -79,7 +109,7 @@ private constructor( Type.DESTROY -> DESTROY_EVENT else -> throw IllegalArgumentException( - "Use the createOn${type.name.toLowerCase().capitalize()}Event() method for this type!", + "Use the createOn${type.name.lowercase().replaceFirstChar(Char::titlecase)}Event() method for this type!", ) } } From 5b9b66e54d70d158caf490001e8b6b0740cd64bf Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Sun, 14 May 2023 12:08:12 -0300 Subject: [PATCH 03/52] Make use of `jvmToolchain` for compiling the project. This allows for a more repeatable, predictable build between different local configurations. --- android/demos/compose/build.gradle | 4 ++++ android/demos/flipper/build.gradle | 6 ++++++ android/demos/intellij/build.gradle | 6 ++++++ android/demos/memory-leaks/build.gradle | 7 +++++++ android/demos/rib-workers/build.gradle | 4 ++++ android/libraries/rib-android-compose/build.gradle | 9 +++++---- android/libraries/rib-android-core/build.gradle | 9 +++++---- android/libraries/rib-android/build.gradle | 9 +++++---- android/libraries/rib-base/build.gradle | 10 ++++------ android/libraries/rib-compiler-app/build.gradle | 10 ++++------ android/libraries/rib-compiler-test/build.gradle | 4 ++++ android/libraries/rib-coroutines-test/build.gradle | 10 ++++------ android/libraries/rib-coroutines/build.gradle | 10 ++++------ android/libraries/rib-debug-utils/build.gradle | 10 ++++------ android/libraries/rib-router-navigator/build.gradle | 10 ++++------ android/libraries/rib-screen-stack-base/build.gradle | 9 +++++---- android/libraries/rib-test/build.gradle | 10 ++++------ android/libraries/rib-workflow-test/build.gradle | 9 +++++---- android/libraries/rib-workflow/build.gradle | 9 +++++---- android/tooling/rib-flipper-plugin/build.gradle | 5 +++++ android/tooling/rib-intellij-plugin/build.gradle | 5 ++++- .../tooling/utils/intellij-broadcast-core/build.gradle | 6 ++++++ android/tutorials/tutorial1/build.gradle | 9 ++++++++- android/tutorials/tutorial2/build.gradle | 9 ++++++++- android/tutorials/tutorial3-completed/build.gradle | 6 ++++++ android/tutorials/tutorial3/build.gradle | 6 ++++++ android/tutorials/tutorial4/build.gradle | 6 ++++++ 27 files changed, 138 insertions(+), 69 deletions(-) diff --git a/android/demos/compose/build.gradle b/android/demos/compose/build.gradle index 943c8a327..2b8d1cf83 100644 --- a/android/demos/compose/build.gradle +++ b/android/demos/compose/build.gradle @@ -2,6 +2,10 @@ apply plugin: 'com.android.application' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "org.jetbrains.kotlin.kapt" +kotlin { + jvmToolchain(11) +} + android { namespace "com.uber.rib.compose" compileSdk deps.build.compileSdkVersion diff --git a/android/demos/flipper/build.gradle b/android/demos/flipper/build.gradle index 5be99aee7..8d957e0db 100644 --- a/android/demos/flipper/build.gradle +++ b/android/demos/flipper/build.gradle @@ -22,6 +22,12 @@ buildscript { apply plugin: 'com.android.application' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.rib.flipper" compileSdk deps.build.compileSdkVersion diff --git a/android/demos/intellij/build.gradle b/android/demos/intellij/build.gradle index bf8a16d0f..083d2e8c6 100644 --- a/android/demos/intellij/build.gradle +++ b/android/demos/intellij/build.gradle @@ -22,6 +22,12 @@ buildscript { apply plugin: 'com.android.application' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.rib.intellij" compileSdk deps.build.compileSdkVersion diff --git a/android/demos/memory-leaks/build.gradle b/android/demos/memory-leaks/build.gradle index e822b4dc4..97f3764d3 100644 --- a/android/demos/memory-leaks/build.gradle +++ b/android/demos/memory-leaks/build.gradle @@ -29,6 +29,13 @@ buildscript { } apply plugin: 'com.android.application' + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + apply plugin: 'net.ltgt.errorprone' apply plugin: 'net.ltgt.nullaway' diff --git a/android/demos/rib-workers/build.gradle b/android/demos/rib-workers/build.gradle index de78a8a04..86cfa6a9d 100644 --- a/android/demos/rib-workers/build.gradle +++ b/android/demos/rib-workers/build.gradle @@ -2,6 +2,10 @@ apply plugin: 'com.android.application' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "org.jetbrains.kotlin.kapt" +kotlin { + jvmToolchain(11) +} + android { namespace "com.uber.rib.workers" compileSdk deps.build.compileSdkVersion diff --git a/android/libraries/rib-android-compose/build.gradle b/android/libraries/rib-android-compose/build.gradle index a9fefc752..420d79ad9 100644 --- a/android/libraries/rib-android-compose/build.gradle +++ b/android/libraries/rib-android-compose/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.android.compose" compileSdk deps.build.compileSdkVersion @@ -53,7 +58,3 @@ dependencies { testImplementation deps.test.mockitoKotlin testImplementation project(":libraries:rib-test") } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-android-core/build.gradle b/android/libraries/rib-android-core/build.gradle index 45bb02a46..bd39b8e09 100644 --- a/android/libraries/rib-android-core/build.gradle +++ b/android/libraries/rib-android-core/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.android.core" compileSdk deps.build.compileSdkVersion @@ -49,7 +54,3 @@ dependencies { testImplementation deps.androidx.appcompat testImplementation deps.external.roboelectricBase } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-android/build.gradle b/android/libraries/rib-android/build.gradle index a5b2275da..378f27102 100644 --- a/android/libraries/rib-android/build.gradle +++ b/android/libraries/rib-android/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'com.vanniktech.maven.publish' +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.android" compileSdk deps.build.compileSdkVersion @@ -56,7 +61,3 @@ dependencies { testImplementation deps.test.mockitoKotlin testImplementation project(":libraries:rib-test") } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-base/build.gradle b/android/libraries/rib-base/build.gradle index 8e9df1a21..5b2051290 100644 --- a/android/libraries/rib-base/build.gradle +++ b/android/libraries/rib-base/build.gradle @@ -24,8 +24,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "org.jetbrains.kotlin.kapt" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { // RIBs themselves don't need to use dagger. But the base library does use dagger @@ -63,7 +65,3 @@ dependencies { transitive = false } } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-compiler-app/build.gradle b/android/libraries/rib-compiler-app/build.gradle index eab2d7ad0..4280f7227 100644 --- a/android/libraries/rib-compiler-app/build.gradle +++ b/android/libraries/rib-compiler-app/build.gradle @@ -17,8 +17,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { implementation project(":libraries:rib-base") @@ -34,10 +36,6 @@ dependencies { testImplementation deps.test.compileTesting } -kotlin { - explicitApi() -} - // https://code.google.com/p/android/issues/detail?id=64887 task copyTestResources(type: Copy) { from "${projectDir}/src/test/resources" diff --git a/android/libraries/rib-compiler-test/build.gradle b/android/libraries/rib-compiler-test/build.gradle index e32433c75..ff3a8913c 100644 --- a/android/libraries/rib-compiler-test/build.gradle +++ b/android/libraries/rib-compiler-test/build.gradle @@ -20,6 +20,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "org.jetbrains.kotlin.kapt" apply plugin: "com.vanniktech.maven.publish" +kotlin { + jvmToolchain(11) +} + dependencies { implementation project(":libraries:rib-compiler-app") implementation deps.apt.javapoet diff --git a/android/libraries/rib-coroutines-test/build.gradle b/android/libraries/rib-coroutines-test/build.gradle index 3ad9692d3..fa0d1c4a3 100644 --- a/android/libraries/rib-coroutines-test/build.gradle +++ b/android/libraries/rib-coroutines-test/build.gradle @@ -18,8 +18,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { api project(':libraries:rib-coroutines') @@ -40,7 +42,3 @@ dependencies { testImplementation deps.apt.androidApi } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-coroutines/build.gradle b/android/libraries/rib-coroutines/build.gradle index 0e9d206f4..6772e817f 100644 --- a/android/libraries/rib-coroutines/build.gradle +++ b/android/libraries/rib-coroutines/build.gradle @@ -18,8 +18,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { @@ -39,7 +41,3 @@ dependencies { testImplementation deps.androidx.annotations testImplementation deps.apt.androidApi } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-debug-utils/build.gradle b/android/libraries/rib-debug-utils/build.gradle index e81da5a3a..9176d5013 100644 --- a/android/libraries/rib-debug-utils/build.gradle +++ b/android/libraries/rib-debug-utils/build.gradle @@ -17,13 +17,11 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { implementation project(":libraries:rib-base") } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-router-navigator/build.gradle b/android/libraries/rib-router-navigator/build.gradle index 78ff35956..41dd11678 100644 --- a/android/libraries/rib-router-navigator/build.gradle +++ b/android/libraries/rib-router-navigator/build.gradle @@ -17,8 +17,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { implementation deps.external.checkerQual @@ -33,7 +35,3 @@ dependencies { testImplementation deps.test.mockitoKotlin testImplementation deps.test.truth } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-screen-stack-base/build.gradle b/android/libraries/rib-screen-stack-base/build.gradle index c0e91d466..04786e7f4 100644 --- a/android/libraries/rib-screen-stack-base/build.gradle +++ b/android/libraries/rib-screen-stack-base/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.ubercab.core.screenstack.base" compileSdk deps.build.compileSdkVersion @@ -45,7 +50,3 @@ dependencies { implementation deps.kotlin.coroutinesAndroid implementation deps.kotlin.coroutinesRx2 } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-test/build.gradle b/android/libraries/rib-test/build.gradle index edeac509e..4f0921003 100644 --- a/android/libraries/rib-test/build.gradle +++ b/android/libraries/rib-test/build.gradle @@ -17,8 +17,10 @@ apply plugin: "org.jetbrains.kotlin.jvm" apply plugin: "com.vanniktech.maven.publish" -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +kotlin { + explicitApi() + jvmToolchain(11) +} dependencies { api project(":libraries:rib-base") @@ -29,7 +31,3 @@ dependencies { api deps.test.mockito implementation deps.test.mockitoKotlin } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-workflow-test/build.gradle b/android/libraries/rib-workflow-test/build.gradle index 06c1fe062..577534ea4 100644 --- a/android/libraries/rib-workflow-test/build.gradle +++ b/android/libraries/rib-workflow-test/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.workflow.test" compileSdk deps.build.compileSdkVersion @@ -50,7 +55,3 @@ dependencies { implementation deps.androidx.annotations implementation deps.test.truth } - -kotlin { - explicitApi() -} diff --git a/android/libraries/rib-workflow/build.gradle b/android/libraries/rib-workflow/build.gradle index 8f4c9a122..97a3b5f22 100644 --- a/android/libraries/rib-workflow/build.gradle +++ b/android/libraries/rib-workflow/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.workflow" compileSdk deps.build.compileSdkVersion @@ -47,7 +52,3 @@ dependencies { testImplementation deps.test.truth testImplementation deps.test.mockito } - -kotlin { - explicitApi() -} diff --git a/android/tooling/rib-flipper-plugin/build.gradle b/android/tooling/rib-flipper-plugin/build.gradle index ae81184fd..543f014bd 100644 --- a/android/tooling/rib-flipper-plugin/build.gradle +++ b/android/tooling/rib-flipper-plugin/build.gradle @@ -18,6 +18,11 @@ apply plugin: 'com.android.library' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: "com.vanniktech.maven.publish" +kotlin { + explicitApi() + jvmToolchain(11) +} + android { namespace "com.uber.rib.flipper" compileSdk deps.build.compileSdkVersion diff --git a/android/tooling/rib-intellij-plugin/build.gradle b/android/tooling/rib-intellij-plugin/build.gradle index 5981a65fc..4a553c7f1 100644 --- a/android/tooling/rib-intellij-plugin/build.gradle +++ b/android/tooling/rib-intellij-plugin/build.gradle @@ -4,10 +4,13 @@ buildscript { } } -apply plugin: "java" apply plugin: "org.jetbrains.intellij" apply plugin: "org.jetbrains.kotlin.jvm" +kotlin { + jvmToolchain(11) +} + group "com.uber.rib" repositories { diff --git a/android/tooling/utils/intellij-broadcast-core/build.gradle b/android/tooling/utils/intellij-broadcast-core/build.gradle index efb6442ec..4bd49ceee 100644 --- a/android/tooling/utils/intellij-broadcast-core/build.gradle +++ b/android/tooling/utils/intellij-broadcast-core/build.gradle @@ -17,6 +17,12 @@ apply plugin: 'com.android.library' apply plugin: "com.vanniktech.maven.publish" +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.debug.broadcast.core" compileSdk deps.build.compileSdkVersion diff --git a/android/tutorials/tutorial1/build.gradle b/android/tutorials/tutorial1/build.gradle index 2eae422aa..7be775dab 100644 --- a/android/tutorials/tutorial1/build.gradle +++ b/android/tutorials/tutorial1/build.gradle @@ -22,9 +22,16 @@ buildscript { } } -apply plugin: 'net.ltgt.errorprone' apply plugin: 'com.android.application' + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + apply plugin: 'net.ltgt.nullaway' +apply plugin: 'net.ltgt.errorprone' android { namespace "com.uber.rib.tutorial1" diff --git a/android/tutorials/tutorial2/build.gradle b/android/tutorials/tutorial2/build.gradle index faaecc022..7b5908ff0 100644 --- a/android/tutorials/tutorial2/build.gradle +++ b/android/tutorials/tutorial2/build.gradle @@ -22,8 +22,15 @@ buildscript { } } -apply plugin: 'net.ltgt.errorprone' apply plugin: 'com.android.application' + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +apply plugin: 'net.ltgt.errorprone' apply plugin: 'net.ltgt.nullaway' android { diff --git a/android/tutorials/tutorial3-completed/build.gradle b/android/tutorials/tutorial3-completed/build.gradle index fef7cd446..c7cb7a869 100644 --- a/android/tutorials/tutorial3-completed/build.gradle +++ b/android/tutorials/tutorial3-completed/build.gradle @@ -22,6 +22,12 @@ buildscript { apply plugin: 'com.android.application' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.rib.tutorial1" compileSdk deps.build.compileSdkVersion diff --git a/android/tutorials/tutorial3/build.gradle b/android/tutorials/tutorial3/build.gradle index 6f080bb75..3b2b91025 100644 --- a/android/tutorials/tutorial3/build.gradle +++ b/android/tutorials/tutorial3/build.gradle @@ -22,6 +22,12 @@ buildscript { apply plugin: 'com.android.application' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.rib.tutorial1" compileSdk deps.build.compileSdkVersion diff --git a/android/tutorials/tutorial4/build.gradle b/android/tutorials/tutorial4/build.gradle index 91da6b818..ca89895fd 100644 --- a/android/tutorials/tutorial4/build.gradle +++ b/android/tutorials/tutorial4/build.gradle @@ -22,6 +22,12 @@ buildscript { apply plugin: 'com.android.application' +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + android { namespace "com.uber.rib.tutorial4" compileSdk deps.build.compileSdkVersion From 391d6dde08c66622cc7e50a50e042f8981fcf090 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Thu, 18 May 2023 07:41:27 -0300 Subject: [PATCH 04/52] Apply explicit visibility on missing classes --- .../kotlin/com/uber/rib/compiler/Constants.kt | 10 +++---- .../rib/compiler/InteractorTestGenerator.kt | 2 +- .../com/uber/rib/compiler/RibTestProcessor.kt | 2 +- .../com/uber/rib/workflow/core/StepTester.kt | 2 ++ .../android/rib/AndroidDeviceRepository.kt | 12 ++++---- .../rib/AttachRibProjectServiceActivity.kt | 2 +- .../plugin/android/rib/CommandLineUtils.kt | 12 ++++---- .../plugin/android/rib/RibHierarchyBrowser.kt | 20 ++++++------- .../plugin/android/rib/RibHierarchyPanel.kt | 8 ++--- .../plugin/android/rib/RibHierarchyUtils.kt | 30 +++++++++---------- .../plugin/android/rib/RibProjectService.kt | 25 ++++++++-------- .../plugin/android/rib/RibViewBrowser.kt | 18 +++++------ .../plugin/android/rib/io/AckRequest.kt | 5 ++-- .../android/rib/io/LogcatMessageDecoder.kt | 8 ++--- .../android/rib/io/LogcatRequestProcessor.kt | 4 +-- .../intellij/plugin/android/rib/io/Request.kt | 14 ++++----- .../plugin/android/rib/io/RequestProcessor.kt | 4 +-- .../plugin/android/rib/io/Response.kt | 10 +++---- .../android/rib/io/RibHierarchyRequest.kt | 14 ++++----- .../android/rib/io/RibHighlightRequest.kt | 4 +-- .../plugin/android/rib/io/RibLocateRequest.kt | 8 ++--- .../android/rib/ui/HierarchyBrowserBase.kt | 12 ++++---- .../rib/ui/RibHierarchyActivityDescriptor.kt | 4 +-- .../ui/RibHierarchyApplicationDescriptor.kt | 4 +-- .../android/rib/ui/RibHierarchyDescriptor.kt | 14 ++++----- .../rib/ui/RibHierarchyNodeDescriptor.kt | 4 +-- .../rib/ui/RibHierarchyRootNodeDescriptor.kt | 14 ++++----- .../rib/ui/RibHierarchyTreeStructure.kt | 6 ++-- .../android/rib/ui/RibViewNodeDescriptor.kt | 6 ++-- .../rib/ui/RibViewRootNodeDescriptor.kt | 2 +- 30 files changed, 144 insertions(+), 136 deletions(-) diff --git a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/Constants.kt b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/Constants.kt index fcc4c446e..1797e1018 100644 --- a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/Constants.kt +++ b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/Constants.kt @@ -16,10 +16,10 @@ package com.uber.rib.compiler /** Constant values used by the annotation processor. */ -open class Constants { - companion object { - const val INTERACTOR_TEST_CREATOR_PREFIX = "Test" - const val INTERACTOR_TEST_CREATOR_SUFFIX = "Interactor" - const val INTERACTOR_TEST_CREATOR_METHOD_NAME = "create" +public open class Constants { + public companion object { + public const val INTERACTOR_TEST_CREATOR_PREFIX: String = "Test" + public const val INTERACTOR_TEST_CREATOR_SUFFIX: String = "Interactor" + public const val INTERACTOR_TEST_CREATOR_METHOD_NAME: String = "create" } } diff --git a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/InteractorTestGenerator.kt b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/InteractorTestGenerator.kt index 9cba1c24f..3f626e9a3 100644 --- a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/InteractorTestGenerator.kt +++ b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/InteractorTestGenerator.kt @@ -33,7 +33,7 @@ import javax.lang.model.element.Modifier * @Scope * @Retention(SOURCE) public @interface LoggedInScope { } */ -open class InteractorTestGenerator( +public open class InteractorTestGenerator( processingEnvironment: ProcessingEnvironment, errorReporter: ErrorReporter, ) : Generator(processingEnvironment, errorReporter) { diff --git a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/RibTestProcessor.kt b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/RibTestProcessor.kt index 0f3013c1d..e982f7c86 100644 --- a/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/RibTestProcessor.kt +++ b/android/libraries/rib-compiler-test/src/main/kotlin/com/uber/rib/compiler/RibTestProcessor.kt @@ -23,7 +23,7 @@ import javax.annotation.processing.Processor /** Process the annotations for all added annotation processor pipelines. */ @AutoService(Processor::class) -open class RibTestProcessor : RibProcessor() { +public open class RibTestProcessor : RibProcessor() { private var interactorTestGenerator: InteractorTestGenerator? = null @Synchronized 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 7752ff537..fb7310c25 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,6 +13,8 @@ * 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 diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AndroidDeviceRepository.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AndroidDeviceRepository.kt index 1d4080c88..c59ef06f9 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AndroidDeviceRepository.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AndroidDeviceRepository.kt @@ -26,7 +26,7 @@ import org.jetbrains.android.sdk.AndroidSdkUtils /** IntelliJ Project component responsible for exposing connected Android devices. */ @Service(PROJECT) -class AndroidDeviceRepository(val project: Project) : +public class AndroidDeviceRepository(public val project: Project) : AndroidDebugBridge.IDeviceChangeListener, Disposable { private val devices: ArrayList = arrayListOf() @@ -60,7 +60,7 @@ class AndroidDeviceRepository(val project: Project) : } @Synchronized - fun addListener(listener: Listener) { + public fun addListener(listener: Listener) { listeners.add(listener) if (devices.size > 0) { listener.onAvailableDevicesChanged(devices) @@ -68,7 +68,7 @@ class AndroidDeviceRepository(val project: Project) : } @Synchronized - fun removeListener(listener: Listener) { + public fun removeListener(listener: Listener) { if (listeners.contains(listener)) { listeners.remove(listener) } @@ -79,7 +79,7 @@ class AndroidDeviceRepository(val project: Project) : listeners.forEach { it.onAvailableDevicesChanged(devices) } } - fun isBridgeConnected(): Boolean { + public fun isBridgeConnected(): Boolean { val debugBridge = AndroidDebugBridge.getBridge() return debugBridge != null && with(debugBridge) { isConnected && hasInitialDeviceList() } } @@ -89,8 +89,8 @@ class AndroidDeviceRepository(val project: Project) : devices.clear() } - interface Listener { + public interface Listener { - fun onAvailableDevicesChanged(devices: List) + public fun onAvailableDevicesChanged(devices: List) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AttachRibProjectServiceActivity.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AttachRibProjectServiceActivity.kt index c6aa2c72e..5fc0c5e4a 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AttachRibProjectServiceActivity.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/AttachRibProjectServiceActivity.kt @@ -19,7 +19,7 @@ import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.startup.StartupActivity -class AttachRibProjectServiceActivity : StartupActivity.Background { +public class AttachRibProjectServiceActivity : StartupActivity.Background { override fun runActivity(project: Project) { project.service().attach() } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/CommandLineUtils.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/CommandLineUtils.kt index 3e297ff9f..49f9d79ff 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/CommandLineUtils.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/CommandLineUtils.kt @@ -26,7 +26,7 @@ import java.nio.charset.Charset import java.util.LinkedList /** Some command line utilities. */ -object CommandLineUtils { +public object CommandLineUtils { /** * Executes `which` for a command. @@ -38,7 +38,7 @@ object CommandLineUtils { * @throws ExecutionException * @throws IOException */ - fun which(project: Project, command: String): String { + public fun which(project: Project, command: String): String { return executeWithLineOutput(project, "which", command).output()[0] } @@ -53,7 +53,7 @@ object CommandLineUtils { * @throws ExecutionException * @throws IOException */ - fun executeWithLineOutput( + public fun executeWithLineOutput( project: Project, command: String, vararg params: String, @@ -93,15 +93,15 @@ object CommandLineUtils { } /** Holder for process output and error stream. */ - class ProcessOutput(private val output: List, private val error: List) { + public class ProcessOutput(private val output: List, private val error: List) { /** Returns the process std output */ - fun output(): List { + public fun output(): List { return output } /** Returns the process error output */ - fun error(): List { + public fun error(): List { return error } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyBrowser.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyBrowser.kt index 0c47e3808..e402dcc84 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyBrowser.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyBrowser.kt @@ -54,22 +54,22 @@ import javax.swing.tree.DefaultMutableTreeNode /** UI component used to render tree of Ribs. */ @SuppressWarnings("TooManyFunctions") -class RibHierarchyBrowser( +public class RibHierarchyBrowser( project: Project, initialModel: Model, private val rootElement: PsiElement, private val selectionListener: Listener?, ) : HierarchyBrowserBase(project, rootElement) { - companion object { + public companion object { /** Go to previous Rib label */ - const val LABEL_GO_PREVIOUS_RIB: String = "Go to previous Scope." + public const val LABEL_GO_PREVIOUS_RIB: String = "Go to previous Scope." /** Go to next Rib label */ - const val LABEL_GO_NEXT_RIB: String = "Go to next Scope" + public const val LABEL_GO_NEXT_RIB: String = "Go to next Scope" /** Type of the Rib hierarchy */ - const val TYPE_HIERARCHY_TYPE: String = "Ribs" + public const val TYPE_HIERARCHY_TYPE: String = "Ribs" private const val ENABLE_LOCATE_MODE: String = "Enable selecting RIB on device" @@ -78,7 +78,7 @@ class RibHierarchyBrowser( } /** Enum used to represent the status of the component */ - enum class Status { + public enum class Status { UNINITIALIZED, INITIALIZING, INITIALIZED, @@ -92,7 +92,7 @@ class RibHierarchyBrowser( * @param selectedRibId the RIB ID of the RIB selected by user (if any) * @param selectedViewId the view ID of the view selected by user (if any) */ - data class Model( + public data class Model( val host: RibHost, val selectedRibId: String = "", val selectedViewId: String = "", @@ -214,7 +214,7 @@ class RibHierarchyBrowser( } /** Request to update hierarchy with provided model */ - fun onModelUpdated(model: Model) { + public fun onModelUpdated(model: Model) { this.status = Status.INITIALIZED this.model = model this.refreshComplete = false @@ -299,9 +299,9 @@ class RibHierarchyBrowser( * Interface used to notify that an new element was selected in {@ScopeHierarchyBrowser} * component. */ - interface Listener { + public interface Listener { /** Callback indicating the selected Rib has changed. */ - fun onSelectedRibChanged(id: UUID) + public fun onSelectedRibChanged(id: UUID) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyPanel.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyPanel.kt index 8163fb38b..81435d444 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyPanel.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyPanel.kt @@ -38,14 +38,14 @@ import javax.swing.JSplitPane import javax.swing.JSplitPane.RIGHT /** UI Component representing the panel including rib hierarchy. */ -class RibHierarchyPanel(val project: Project, private val initialModel: Model) : +public class RibHierarchyPanel(public val project: Project, private val initialModel: Model) : JPanel(), RibProjectService.Listener, ActionListener, RibHierarchyBrowser.Listener, RibViewBrowser.Listener { - companion object { + public companion object { private val EMPTY_RIB_VIEW: RibView = RibView("", "", "", "", emptyList()) private val EMPTY_RIB_NODE: RibNode = RibNode("", "", emptyList(), EMPTY_RIB_VIEW) private val EMPTY_VIEW_MODEL: ViewModel = @@ -113,13 +113,13 @@ class RibHierarchyPanel(val project: Project, private val initialModel: Model) : } /** Requests to update the list of devices. */ - fun onAvailableDevicesChanged(devices: List) { + public fun onAvailableDevicesChanged(devices: List) { comboBoxModel.removeAllElements() devices.forEach { comboBoxModel.addElement(it) } } /** Requests to update the selected device. */ - fun onSelectedDeviceChanged(selectedDevice: IDevice?) { + public fun onSelectedDeviceChanged(selectedDevice: IDevice?) { comboBoxModel.selectedItem = selectedDevice } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyUtils.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyUtils.kt index 77c177da9..70640aaca 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyUtils.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibHierarchyUtils.kt @@ -56,14 +56,14 @@ import java.util.UUID /** Utility class used by the Rib hierarchy browser component. */ @SuppressWarnings("TooManyFunctions") -class RibHierarchyUtils { +public class RibHierarchyUtils { private constructor() - companion object { + public companion object { /** Constant used to represent empty UUID */ - val EMPTY_UUID: UUID = UUID(0, 0) + public val EMPTY_UUID: UUID = UUID(0, 0) /** Time for balloon to fade out */ private const val BALLOON_FADE_OUT_TIME: Long = 3000 @@ -72,7 +72,7 @@ class RibHierarchyUtils { private const val LAYOUT_FOLDER_NAME: String = "layout" /** Build root element, used when class is not available. */ - fun buildRootElement(project: Project): PsiClass { + public fun buildRootElement(project: Project): PsiClass { val psiClass: PsiClass? = JavaPsiFacade.getInstance(project) .findClass(Object::class.java.name, GlobalSearchScope.allScope(project)) @@ -81,32 +81,32 @@ class RibHierarchyUtils { } /** Return the psiClass corresponding to the given class name. */ - fun getPsiClass(project: Project, name: String): PsiClass { + public fun getPsiClass(project: Project, name: String): PsiClass { val psiClass: PsiClass? = JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project)) return psiClass ?: buildRootElement(project) } /** Check if the element supplied is a root element. */ - fun isRootElement(element: PsiElement?): Boolean { + public fun isRootElement(element: PsiElement?): Boolean { return element is PsiClass && element.qualifiedName == Object::class.java.name } /** Format fully qualified class name. */ - fun formatQualifiedName(qualifiedName: String): String { + public fun formatQualifiedName(qualifiedName: String): String { val index: Int = qualifiedName.lastIndexOf(".") return if (index > 0) qualifiedName.substring(0, index) else qualifiedName } /** Format fully qualified class name. */ - fun formatSimpleName(qualifiedName: String): String { + public fun formatSimpleName(qualifiedName: String): String { val index: Int = qualifiedName.lastIndexOf(".") return if (index > 0) qualifiedName.substring(index + 1) else qualifiedName } /** Find node with the given ID in node hierarchy */ @SuppressWarnings("ReturnCount") - fun findRibNodeRecursive(ribNode: RibNode?, id: UUID): RibNode? { + public fun findRibNodeRecursive(ribNode: RibNode?, id: UUID): RibNode? { if (ribNode == null) { return null } @@ -124,7 +124,7 @@ class RibHierarchyUtils { /** Find view with the given ID in view hierarchy */ @SuppressWarnings("ReturnCount") - fun findRibViewRecursive(ribView: RibView?, id: UUID): RibView? { + public fun findRibViewRecursive(ribView: RibView?, id: UUID): RibView? { if (ribView == null) { return null } @@ -141,7 +141,7 @@ class RibHierarchyUtils { } /** Get a view tag value suffix */ - fun getTagValueSuffix(value: String?): String? { + public fun getTagValueSuffix(value: String?): String? { if (value == null) { return null } @@ -151,21 +151,21 @@ class RibHierarchyUtils { /** Get virtual file from path */ @SuppressWarnings("MagicNumber") - fun getVirtualFile(filePath: String): VirtualFile? { + public fun getVirtualFile(filePath: String): VirtualFile? { val actualPath: Path = Paths.get(filePath) val pathFile: File = actualPath.toFile() return LocalFileSystem.getInstance().findFileByIoFile(pathFile) } /** Returns whether virtual file belongs to project and appears to be a layout file */ - fun isProjectLayoutFile(project: Project, file: VirtualFile): Boolean { + public fun isProjectLayoutFile(project: Project, file: VirtualFile): Boolean { return ProjectRootManager.getInstance(project).fileIndex.isInContent(file) && file.fileType is XmlFileType && file.path.contains("/$LAYOUT_FOLDER_NAME/") } /** Display popup balloon. */ - fun displayPopup( + public fun displayPopup( message: String, location: RelativePoint, type: MessageType = MessageType.WARNING, @@ -178,7 +178,7 @@ class RibHierarchyUtils { } /** Display notification bubble. */ - fun log(message: String) { + public fun log(message: String) { Notifications.Bus.notify(Notification("Rib", "Rib", message, NotificationType.INFORMATION)) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibProjectService.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibProjectService.kt index edd0e288a..f2aeb9526 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibProjectService.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibProjectService.kt @@ -48,9 +48,10 @@ import java.util.concurrent.Executor import java.util.concurrent.Executors @Service(PROJECT) -class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener, Disposable { +public class RibProjectService(public val project: Project) : + AndroidDeviceRepository.Listener, Disposable { - companion object { + public companion object { private const val TOOL_WINDOW_ID: String = "Ribs" private const val TOOL_WINDOW_TITLE: String = "Ribs" private const val TAB_NAME_RIBS: String = "Hierarchy" @@ -69,7 +70,7 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener private var isRefreshing: Boolean = false private var isLocating: Boolean = false - fun attach() { + public fun attach() { DumbService.getInstance(project).runWhenSmart { ApplicationManager.getApplication().runReadAction { androidDeviceRepository.addListener(this) @@ -78,7 +79,7 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener } } - fun refreshRibHierarchy() { + public fun refreshRibHierarchy() { if (isRefreshing) { return } @@ -119,21 +120,21 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener } } - fun highlightRib(id: UUID) { + public fun highlightRib(id: UUID) { val device: IDevice = selectedDevice ?: return LogcatRequestProcessor().execute(RibHighlightRequest(device, id)) } - fun highlightView(id: UUID) { + public fun highlightView(id: UUID) { val device: IDevice = selectedDevice ?: return LogcatRequestProcessor().execute(RibHighlightRequest(device, id)) } - fun isLocating(): Boolean { + public fun isLocating(): Boolean { return isLocating } - fun enableLocateMode() { + public fun enableLocateMode() { if (isLocating) { return } @@ -176,7 +177,7 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener ) } - fun selectDevice(device: IDevice?) { + public fun selectDevice(device: IDevice?) { if (selectedDevice == device) { return } @@ -190,7 +191,7 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener refreshRibHierarchy() } - fun hasSelectedDevice(): Boolean { + public fun hasSelectedDevice(): Boolean { return selectedDevice != null } @@ -236,7 +237,7 @@ class RibProjectService(val project: Project) : AndroidDeviceRepository.Listener return content } - interface Listener { - fun onModelUpdated(model: RibHierarchyBrowser.Model) + public interface Listener { + public fun onModelUpdated(model: RibHierarchyBrowser.Model) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibViewBrowser.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibViewBrowser.kt index 84a25d9f3..b9d728d05 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibViewBrowser.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/RibViewBrowser.kt @@ -45,22 +45,22 @@ import kotlin.Comparator /** UI component used to render tree of Ribs. */ @SuppressWarnings("TooManyFunctions") -class RibViewBrowser( +public class RibViewBrowser( project: Project, private val model: Model, private val rootElement: PsiElement, private val selectionListener: Listener?, ) : HierarchyBrowserBase(project, rootElement) { - companion object { + public companion object { /** Go to previous Rib label */ - const val LABEL_GO_PREVIOUS_RIB: String = "Go to previous Scope." + public const val LABEL_GO_PREVIOUS_RIB: String = "Go to previous Scope." /** Go to next Rib label */ - const val LABEL_GO_NEXT_RIB: String = "Go to next Scope" + public const val LABEL_GO_NEXT_RIB: String = "Go to next Scope" /** Type of the Rib hierarchy */ - const val TYPE_HIERARCHY_TYPE: String = "Views" + public const val TYPE_HIERARCHY_TYPE: String = "Views" } /** @@ -72,7 +72,7 @@ class RibViewBrowser( * @param selectedRibId the RIB ID of the RIB selected by user (if any) * @param selectedViewId the view ID of the view selected by user (if any) */ - data class Model( + public data class Model( val ribNode: RibNode, val ribView: RibView, val rootRib: RibNode, @@ -173,7 +173,7 @@ class RibViewBrowser( } /** Notify that the currently selected view has changed. */ - fun notifySelectedViewChanged() { + public fun notifySelectedViewChanged() { val node: Any? = currentTree.lastSelectedPathComponent if (node is DefaultMutableTreeNode && hasFocus) { val descriptor = node.userObject @@ -190,9 +190,9 @@ class RibViewBrowser( /** * Interface used to notify that a new view was selected in {@ScopeHierarchyBrowser} component. */ - interface Listener { + public interface Listener { /** Callback indicating the selected View has changed. */ - fun onSelectedViewChanged(ribView: RibView) + public fun onSelectedViewChanged(ribView: RibView) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/AckRequest.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/AckRequest.kt index 52406b9bd..fb971691b 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/AckRequest.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/AckRequest.kt @@ -18,7 +18,8 @@ package com.uber.intellij.plugin.android.rib.io import com.android.ddmlib.IDevice /** Ack response object. */ -class AckResponse : Response() +public class AckResponse : Response() /** Ack request object. */ -class AckRequest(device: IDevice) : Request(device, "ACK", AckResponse::class.java) +public class AckRequest(device: IDevice) : + Request(device, "ACK", AckResponse::class.java) diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatMessageDecoder.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatMessageDecoder.kt index e76ec8e84..b95662fea 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatMessageDecoder.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatMessageDecoder.kt @@ -19,7 +19,7 @@ package com.uber.intellij.plugin.android.rib.io * Class responsible for collecting split messages from logcat, and concatenating them into full * message response. This is to work around 4000 char limit of logcat entries. */ -class LogcatMessageDecoder { +public class LogcatMessageDecoder { private inner class MessagePart(message: String) : Comparable { val partNumber: Int @@ -47,13 +47,13 @@ class LogcatMessageDecoder { private var parts: HashMap = HashMap() /** Whether all parts were received to reconstruct message */ - val complete: Boolean + public val complete: Boolean get() { return partCount > 0 && parts.size == partCount } /** The full message. */ - val message: String + public val message: String get() { if (!complete) { error("Message is not complete") @@ -66,7 +66,7 @@ class LogcatMessageDecoder { } /** Method invoked when new part of message are received. */ - fun onMessagePartReceived(message: String) { + public fun onMessagePartReceived(message: String) { val part = MessagePart(message) if (partCount == 0) { partCount = part.partCount diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatRequestProcessor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatRequestProcessor.kt index 4a820a1ff..c6f48f8f6 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatRequestProcessor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/LogcatRequestProcessor.kt @@ -30,8 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger * Implementation of the request processor interface, relying on emitting broadcast and parsing * logcat output. */ -class LogcatRequestProcessor : RequestProcessor { - companion object { +public class LogcatRequestProcessor : RequestProcessor { + public companion object { private const val SHELL_COMMAND_TEMPLATE: String = "am broadcast -a com.uber.debug.intent.action.COMMAND --ei SEQ %d --es CMD %s" private const val LOGCAT_COMMAND_TEMPLATE: String = diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Request.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Request.kt index 1f0eab1c7..461f25632 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Request.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Request.kt @@ -27,11 +27,11 @@ import com.android.ddmlib.IDevice * @param timeoutMs the timeout to add to request * @param numRetries the number of retires to use for this request */ -open class Request( - val device: IDevice, - val command: String, - val clazz: Class, - val params: List> = emptyList(), - val timeoutMs: Int = 2000, - val numRetries: Int = 1, +public open class Request( + public val device: IDevice, + public val command: String, + public val clazz: Class, + public val params: List> = emptyList(), + public val timeoutMs: Int = 2000, + public val numRetries: Int = 1, ) diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RequestProcessor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RequestProcessor.kt index 23d709d08..8dd16397a 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RequestProcessor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RequestProcessor.kt @@ -21,8 +21,8 @@ import com.google.common.util.concurrent.ListenableFuture * Interface used by classes capable of communicating to Android device, i.e send and receive * messages. */ -interface RequestProcessor { +public interface RequestProcessor { /** Send a request to device and returns a future to access response. */ - fun execute(request: Request): ListenableFuture + public fun execute(request: Request): ListenableFuture } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Response.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Response.kt index 23e9455e4..a72c8017e 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Response.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/Response.kt @@ -16,16 +16,16 @@ package com.uber.intellij.plugin.android.rib.io /** Class representing a response. */ -open class Response { +public open class Response { /** Whether request was successful */ - var success: Boolean = false + public var success: Boolean = false /** Protocol version of the response message */ - var version: Int = 0 + public var version: Int = 0 /** Description of the error (if any) */ - var errorDescription: String? = null + public var errorDescription: String? = null /** Payload of the response */ - var payload: T? = null + public var payload: T? = null } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHierarchyRequest.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHierarchyRequest.kt index fe34af370..482b12950 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHierarchyRequest.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHierarchyRequest.kt @@ -18,13 +18,13 @@ package com.uber.intellij.plugin.android.rib.io import com.android.ddmlib.IDevice /** Data class representing the host of a Rib application, i.e an android device. */ -data class RibHost(val name: String, val application: RibApplication?) +public data class RibHost(val name: String, val application: RibApplication?) /** Data class representing a Rib application. */ -data class RibApplication(val name: String, val activities: List) +public data class RibApplication(val name: String, val activities: List) /** Data class representing a Rib activity. */ -data class RibActivity(val name: String, val rootRib: RibNode) +public data class RibActivity(val name: String, val rootRib: RibNode) /** * Data class representing a Rib node. @@ -34,7 +34,7 @@ data class RibActivity(val name: String, val rootRib: RibNode) * @param children the list of children for this node * @param view the view for this rib node */ -data class RibNode( +public data class RibNode( val id: String, val name: String, val children: List, @@ -50,7 +50,7 @@ data class RibNode( * @param layoutId the name of the layout this view was inflated from * @param children the list of children for this view */ -data class RibView( +public data class RibView( val id: String, val name: String, val viewId: String, @@ -59,8 +59,8 @@ data class RibView( ) /** Data class representing the response of the Rib hierarchy request. */ -data class RibHierarchyResponse(val host: RibHost) : Response() +public data class RibHierarchyResponse(val host: RibHost) : Response() /** Data class representing the request for a Rib hierarchy. */ -class RibHierarchyRequest(device: IDevice) : +public class RibHierarchyRequest(device: IDevice) : Request(device, "RIB_HIERARCHY", RibHierarchyResponse::class.java) diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHighlightRequest.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHighlightRequest.kt index 16373aab6..fee14149e 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHighlightRequest.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibHighlightRequest.kt @@ -19,10 +19,10 @@ import com.android.ddmlib.IDevice import java.util.UUID /** Rib highlight response object. */ -class RibHighlightResponse : Response() +public class RibHighlightResponse : Response() /** Rib highlight request object. */ -class RibHighlightRequest(device: IDevice, id: UUID) : +public class RibHighlightRequest(device: IDevice, id: UUID) : Request( device, "RIB_HIGHLIGHT", diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibLocateRequest.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibLocateRequest.kt index f9427204b..c0b9ca931 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibLocateRequest.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/io/RibLocateRequest.kt @@ -25,7 +25,7 @@ import com.android.ddmlib.IDevice * @param selectedRibId the RIB ID of the RIB selected by user * @param selectedViewId the view ID of the view selected by user */ -data class RibHostWithSelection( +public data class RibHostWithSelection( val name: String, val application: RibApplication?, val selectedRibId: String, @@ -37,11 +37,11 @@ data class RibHostWithSelection( * * @param host the host */ -data class RibHierarchyWithSelectionResponse(val host: RibHostWithSelection) : +public data class RibHierarchyWithSelectionResponse(val host: RibHostWithSelection) : Response() /** Rib locate request object. */ -class EnableLocateModeRequest(device: IDevice, enabled: Boolean) : +public class EnableLocateModeRequest(device: IDevice, enabled: Boolean) : Request( device, "RIB_LOCATE", @@ -50,7 +50,7 @@ class EnableLocateModeRequest(device: IDevice, enabled: Boolean) : TIMEOUT_MS, NUM_RETRIES, ) { - companion object { + public companion object { private const val TIMEOUT_MS = 1000 private const val NUM_RETRIES = 5 } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/HierarchyBrowserBase.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/HierarchyBrowserBase.kt index 36fa3f8af..296c835ad 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/HierarchyBrowserBase.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/HierarchyBrowserBase.kt @@ -36,8 +36,10 @@ import javax.swing.tree.TreeSelectionModel * It enables speed search configurations to search non-expanded nodes too. It is needed when * searching for given scope(s) in the entire graph hierarchy (which can be pretty large). */ -abstract class HierarchyBrowserBase(val project: Project, private val rootElement: PsiElement) : - HierarchyBrowserBaseEx(project, rootElement) { +public abstract class HierarchyBrowserBase( + public val project: Project, + private val rootElement: PsiElement, +) : HierarchyBrowserBaseEx(project, rootElement) { override fun doRefresh(currentBuilderOnly: Boolean) { ApplicationManager.getApplication().invokeLater { @@ -69,15 +71,15 @@ abstract class HierarchyBrowserBase(val project: Project, private val rootElemen } /** Refresh completion callback. */ - open fun onRefreshComplete() {} + public open fun onRefreshComplete() {} /** Expand entire hierarchy */ - fun expandAll() { + public fun expandAll() { TreeUtil.expandAll(currentTree) } /** Select a given item in the hierarchy, based on provided id. */ - fun selectById(id: String) { + public fun selectById(id: String) { TreeUtil.promiseSelect( currentTree, object : TreeVisitor { diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyActivityDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyActivityDescriptor.kt index 410014e55..1f612891d 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyActivityDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyActivityDescriptor.kt @@ -27,11 +27,11 @@ import com.uber.intellij.plugin.android.rib.io.RibActivity import javax.swing.Icon /** Node descriptor used to render a Rib activities. */ -class RibHierarchyActivityDescriptor( +public class RibHierarchyActivityDescriptor( project: Project, parentDescriptor: HierarchyNodeDescriptor?, private val clazz: PsiClass, - val ribActivity: RibActivity, + public val ribActivity: RibActivity, ) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyApplicationDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyApplicationDescriptor.kt index 9e7bb4dee..9f7e92329 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyApplicationDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyApplicationDescriptor.kt @@ -25,11 +25,11 @@ import com.uber.intellij.plugin.android.rib.io.RibApplication import javax.swing.Icon /** Node descriptor used to render a Rib Application. */ -class RibHierarchyApplicationDescriptor( +public class RibHierarchyApplicationDescriptor( project: Project, parentDescriptor: HierarchyNodeDescriptor?, private val clazz: PsiClass, - val ribApplication: RibApplication, + public val ribApplication: RibApplication, ) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyDescriptor.kt index 46d372d3f..7dd283c61 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyDescriptor.kt @@ -27,18 +27,18 @@ import java.awt.Font import javax.swing.Icon /** Base class for all tree node descriptors used in Rib IntelliJ plugin. */ -open class RibHierarchyDescriptor( +public open class RibHierarchyDescriptor( project: Project, - val parentDescriptor: HierarchyNodeDescriptor?, - val element: PsiElement, + public val parentDescriptor: HierarchyNodeDescriptor?, + public val element: PsiElement, isBase: Boolean, ) : HierarchyNodeDescriptor(project, parentDescriptor, element, isBase) { /** Method to set text of the node entry. */ - open fun updateText(text: CompositeAppearance) {} + public open fun updateText(text: CompositeAppearance) {} /** Method used to get the unique id of descriptor. Used for programmatic selection. */ - open fun getUniqueId(): String? { + public open fun getUniqueId(): String? { return null } @@ -57,7 +57,7 @@ open class RibHierarchyDescriptor( } /** Return default text attributes. */ - fun getDefaultTextAttributes(isError: Boolean = false): TextAttributes { + public fun getDefaultTextAttributes(isError: Boolean = false): TextAttributes { val font: Int = if (myIsBase) Font.BOLD else Font.PLAIN return if (isError) { TextAttributes(myColor, null, Color.red, EffectType.WAVE_UNDERSCORE, font) @@ -72,7 +72,7 @@ open class RibHierarchyDescriptor( } /** Compare 2 instances of text appearance. */ - fun CompositeAppearance.compareTo(another: CompositeAppearance): Boolean { + public fun CompositeAppearance.compareTo(another: CompositeAppearance): Boolean { return Comparing.equal(this, another) } } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyNodeDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyNodeDescriptor.kt index 84a2c542a..9af94f0ef 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyNodeDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyNodeDescriptor.kt @@ -24,11 +24,11 @@ import com.uber.intellij.plugin.android.rib.RibHierarchyUtils.Companion.formatSi import com.uber.intellij.plugin.android.rib.io.RibNode /** Node descriptor used to render a Rib. */ -class RibHierarchyNodeDescriptor( +public class RibHierarchyNodeDescriptor( project: Project, parentDescriptor: HierarchyNodeDescriptor?, private val clazz: PsiClass, - val ribNode: RibNode, + public val ribNode: RibNode, ) : RibHierarchyDescriptor(project, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyRootNodeDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyRootNodeDescriptor.kt index d71dadf6e..e68a8c503 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyRootNodeDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyRootNodeDescriptor.kt @@ -27,29 +27,29 @@ import com.uber.intellij.plugin.android.rib.io.RibHost import javax.swing.Icon /** Node descriptor used to render tree roots. */ -class RibHierarchyRootNodeDescriptor( +public class RibHierarchyRootNodeDescriptor( project: Project, element: PsiElement, - val ribHost: RibHost, + public val ribHost: RibHost, private val status: RibHierarchyBrowser.Status, ) : RibHierarchyDescriptor(project, null, element, true) { private val deviceRepository: AndroidDeviceRepository = project.service() private val ribProjectService: RibProjectService = project.service() - companion object { + public companion object { /** Label used when android bridge is not connected */ - const val LABEL_NO_BRIDGE: String = + public const val LABEL_NO_BRIDGE: String = "No Android bridge. Make sure Android SDK is configured for this project." /** Label used when no device is connected. */ - const val LABEL_NO_DEVICE: String = "No Android device connected..." + public const val LABEL_NO_DEVICE: String = "No Android device connected..." /** Label used when device list is being refreshed. */ - const val LABEL_WAIT: String = "Loading RIB info..." + public const val LABEL_WAIT: String = "Loading RIB info..." /** Label used when no no Rib info could be fetched from device. */ - const val LABEL_ERROR: String = + public const val LABEL_ERROR: String = "No RIB info available. Make sure RIB app is running in foreground, then refresh." } diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyTreeStructure.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyTreeStructure.kt index fbb58d0dd..fd5e30e0c 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyTreeStructure.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibHierarchyTreeStructure.kt @@ -21,8 +21,10 @@ import com.intellij.openapi.project.Project import com.uber.intellij.plugin.android.rib.RibHierarchyUtils /** Tree structure used by Rib hierarchy */ -class RibHierarchyTreeStructure(private val project: Project, descriptor: HierarchyNodeDescriptor) : - HierarchyTreeStructure(project, descriptor) { +public class RibHierarchyTreeStructure( + private val project: Project, + descriptor: HierarchyNodeDescriptor, +) : HierarchyTreeStructure(project, descriptor) { init { setBaseElement(descriptor) diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewNodeDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewNodeDescriptor.kt index 756eafae7..ca999c083 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewNodeDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewNodeDescriptor.kt @@ -34,11 +34,11 @@ import javax.swing.Icon * @param ribNode the rib node corresponding to this descriptor * @param ribView the rib view corresponding to this descriptor */ -open class RibViewNodeDescriptor( +public open class RibViewNodeDescriptor( project: Project, element: PsiElement, - val ribNode: RibNode, - val ribView: RibView?, + public val ribNode: RibNode, + public val ribView: RibView?, ) : RibHierarchyDescriptor(project, null, element, false) { override fun updateText(text: CompositeAppearance) { diff --git a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewRootNodeDescriptor.kt b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewRootNodeDescriptor.kt index 2843ecf48..59511f4b6 100644 --- a/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewRootNodeDescriptor.kt +++ b/android/tooling/rib-intellij-plugin/src/main/kotlin/com/uber/intellij/plugin/android/rib/ui/RibViewRootNodeDescriptor.kt @@ -32,7 +32,7 @@ import java.awt.Font * @param ribNode the rib node corresponding to this descriptor * @param ribView the rib view corresponding to this descriptor */ -class RibViewRootNodeDescriptor( +public class RibViewRootNodeDescriptor( private val nonNullProject: Project, element: PsiElement, ribNode: RibNode, From 25adeff0913bc62e3909bb1b51d315453973fae2 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Thu, 18 May 2023 07:41:59 -0300 Subject: [PATCH 05/52] Create version catalogs --- android/gradle/libs.versions.toml | 119 +++++++++++++++++++++++++ android/gradle/test-libs.versions.toml | 17 ++++ 2 files changed, 136 insertions(+) create mode 100644 android/gradle/libs.versions.toml create mode 100644 android/gradle/test-libs.versions.toml diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml new file mode 100644 index 000000000..4362e9552 --- /dev/null +++ b/android/gradle/libs.versions.toml @@ -0,0 +1,119 @@ +[versions] +android-api = "4.1.1.4" +androidx-activity = "1.7.0" +androidx-annotations = "1.1.0" +androidx-appcompat = "1.3.0" +androidx-lifecycle = "2.5.1" +autocommon = "0.8" +autodispose = "1.4.0" +autoservice = "1.0-rc4" +autovalue = "1.7" +checkerqual = "2.5.1" +compile-testing = "0.17" +compose-compiler = "1.4.6" +compose-libraries = "1.4.0" +compose-navigation = "2.4.0-alpha03" +coroutines = "1.6.4" +dagger = "2.43.2" +errorprone = "2.3.3" +errorprone-javac = "9+181-r4173-1" +flipper = "0.93.0" +gjf = "1.16.0" +gradle-android-plugin = "7.4.2" +gradle-errorprone-plugin = "1.3.0" +gradle-intellij-plugin = "1.13.1" +gradle-maven-publish-plugin = "0.25.2" +gradle-nullaway-plugin = "1.0.2" +gradle-spotless-plugin = "6.18.0" +gson = "2.8.7" +guava-android = "27.1-android" +guava-jre = "27.1-jre" +intellij = "2022.2.4" +javapoet = "1.11.1" +jsr250 = "1.0" +junit = "4.12" +kotlin = "1.8.20" +kotlinx-coroutines = "1.6.4" +ktfmt = "0.43" +ktlint = "0.48.2" +leakcanary = "1.5.4" +mockito = "4.6.1" +mockito-kotlin = "4.0.0" +motif = "0.3.4" +percent = "1.0.0" +reactivestreams = "1.0.0" +rxandroid2 = "2.1.1" +rxbinding = "2.0.0" +rxjava2 = "2.2.8" +rxkotlin = "2.2.0" +rxrelay2 = "2.1.0" +savedstate = "1.2.0" +truth = "0.43" +uber-nullaway = "0.9.0" + +[libraries] +activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity" } +android-api = { group = "com.google.android", name = "android", version.ref = "android-api" } +annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidx-annotations" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } +autocommon = { group = "com.google.auto", name = "auto-common", version.ref = "autocommon" } +autodispose-coroutines = { group = "com.uber.autodispose", name = "autodispose-coroutines-interop", version.ref = "autodispose" } +autodispose-errorprone = { group = "com.uber.autodispose", name = "autodispose-error-prone", version.ref = "autodispose" } +autodispose-library = { group = "com.uber.autodispose", name = "autodispose", version.ref = "autodispose" } +autodispose-lifecycle = { group = "com.uber.autodispose", name = "autodispose-lifecycle", version.ref = "autodispose" } +autoservice = { group = "com.google.auto.service", name = "auto-service", version.ref = "autoservice" } +autovalue-annotations = { group = "com.google.auto.value", name = "auto-value-annotations", version.ref = "autovalue" } +autovalue-library = { group = "com.google.auto.value", name = "auto-value", version.ref = "autovalue" } +checkerqual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerqual" } +compose-animation = { group = "androidx.compose.animation", name = "animation", version.ref = "compose-libraries" } +compose-compiler = { group = "androidx.compose.compiler", name = "compiler", version.ref = "compose-libraries" } +compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "compose-libraries" } +compose-material = { group = "androidx.compose.material", name = "material", version.ref = "compose-libraries" } +compose-navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "compose-navigation" } +compose-runtime-rx2 = { group = "androidx.compose.runtime", name = "runtime-rxjava2", version.ref = "compose-libraries" } +compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "compose-libraries" } +compose-uitooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "compose-libraries" } +compose-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "compose-libraries" } +coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" } +coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } +coroutines-rx2 = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-rx2", version.ref = "coroutines" } +dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "dagger" } +dagger-library = { group = "com.google.dagger", name = "dagger", version.ref = "dagger" } +errorprone-core = { group = "com.google.errorprone", name = "error_prone_core", version.ref = "errorprone" } +errorprone-javac = { group = "com.google.errorprone", name = "javac", version.ref = "errorprone-javac" } +flipper = { group = "com.facebook.flipper", name = "flipper", version.ref = "flipper" } +gradle-android-plugin = { module = "com.android.tools.build:gradle", version.ref = "gradle-android-plugin" } +gradle-errorprone-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "gradle-errorprone-plugin" } +gradle-kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +gradle-nullaway-plugin = { module = "net.ltgt.gradle:gradle-nullaway-plugin", version.ref = "gradle-nullaway-plugin" } +gradle-spotless-plugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "gradle-spotless-plugin" } +gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } +guava-android = { group = "com.google.guava", name = "guava", version.ref = "guava-android" } +guava-jre = { group = "com.google.guava", name = "guava", version.ref = "guava-jre" } +javapoet = { group = "com.squareup", name = "javapoet", version.ref = "javapoet" } +javax-inject = { group = "javax.inject", name = "javax.inject", version = "1" } +jsr250 = { group = "javax.annotation", name = "jsr250-api", version.ref = "jsr250" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } +leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" } +motif-compiler = { group = "com.uber.motif", name = "motif-compiler", version.ref = "motif" } +motif-library = { group = "com.uber.motif", name = "motif", version.ref = "motif" } +percent = { group = "androidx.percentlayout", name = "percentlayout", version.ref = "percent" } +reactivestreams = { group = "org.reactivestreams", name = "reactive-streams", version.ref = "reactivestreams" } +rxandroid2 = { group = "io.reactivex.rxjava2", name = "rxandroid", version.ref = "rxandroid2" } +rxbinding = { group = "com.jakewharton.rxbinding2", name = "rxbinding", version.ref = "rxbinding" } +rxjava2 = { group = "io.reactivex.rxjava2", name = "rxjava", version.ref = "rxjava2" } +rxkotlin = { group = "io.reactivex.rxjava2", name = "rxkotlin", version.ref = "rxkotlin" } +rxrelay2 = { group = "com.jakewharton.rxrelay2", name = "rxrelay", version.ref = "rxrelay2" } +savedstate = { group = "androidx.savedstate", name = "savedstate", version.ref = "savedstate" } +uber-nullaway = { group = "com.uber.nullaway", name = "nullaway", version.ref = "uber-nullaway" } + +[plugins] +androidApplication = { id = "com.android.application", version.ref = "gradle-android-plugin" } +androidLibrary = { id = "com.android.library", version.ref = "gradle-android-plugin" } +errorprone = { id = "net.ltgt.errorprone", version.ref = "gradle-errorprone-plugin" } +intellij = { id = "org.jetbrains.intellij", version.ref = "gradle-intellij-plugin" } +kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlinKapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } +mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "gradle-maven-publish-plugin" } +nullaway = { id = "net.ltgt.nullaway", version.ref = "gradle-nullaway-plugin" } +spotless = { id = "com.diffplug.spotless", version.ref = "gradle-spotless-plugin" } diff --git a/android/gradle/test-libs.versions.toml b/android/gradle/test-libs.versions.toml new file mode 100644 index 000000000..0b9a3c964 --- /dev/null +++ b/android/gradle/test-libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +compile-testing = "0.17" +coroutines = "1.6.4" +junit = "4.12" +mockito = "4.6.1" +mockito-kotlin = "4.0.0" +robolectric = "4.4" +truth = "0.43" + +[libraries] +compileTesting = { group = "com.google.testing.compile", name = "compile-testing", version.ref = "compile-testing" } +coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +mockito = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" } +mockitoKotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockito-kotlin" } +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } +truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } From 42c58afd0178bff2d24605b2e6a3bb41b79af1bc Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Thu, 18 May 2023 07:43:00 -0300 Subject: [PATCH 06/52] Create precompiled scripts convention plugins to be used as included builds --- android/conventions/build.gradle.kts | 37 +++++++++ android/conventions/settings.gradle.kts | 24 ++++++ .../conventions/src/main/kotlin/Extensions.kt | 47 +++++++++++ ...android-application-conventions.gradle.kts | 79 +++++++++++++++++++ ...lication-errorprone-conventions.gradle.kts | 37 +++++++++ ...lin-android-library-conventions.gradle.kts | 76 ++++++++++++++++++ ...ribs.kotlin-library-conventions.gradle.kts | 30 +++++++ .../ribs.spotless-convention.gradle.kts | 59 ++++++++++++++ 8 files changed, 389 insertions(+) create mode 100644 android/conventions/build.gradle.kts create mode 100644 android/conventions/settings.gradle.kts create mode 100644 android/conventions/src/main/kotlin/Extensions.kt create mode 100644 android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts create mode 100644 android/conventions/src/main/kotlin/ribs.kotlin-android-application-errorprone-conventions.gradle.kts create mode 100644 android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts create mode 100644 android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts create mode 100644 android/conventions/src/main/kotlin/ribs.spotless-convention.gradle.kts diff --git a/android/conventions/build.gradle.kts b/android/conventions/build.gradle.kts new file mode 100644 index 000000000..9f6ae882a --- /dev/null +++ b/android/conventions/build.gradle.kts @@ -0,0 +1,37 @@ +/* + * 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. + */ +plugins { + `kotlin-dsl` + `kotlin-dsl-precompiled-script-plugins` +} + +repositories { + google() + mavenCentral() + gradlePluginPortal() +} + +dependencies { + // Workaround for using version catalog on precompiled scripts. + // https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(gradleApi()) + implementation(libs.gradle.android.plugin) + implementation(libs.gradle.kotlin.plugin) + implementation(libs.gradle.errorprone.plugin) + implementation(libs.gradle.nullaway.plugin) + implementation(libs.gradle.spotless.plugin) +} diff --git a/android/conventions/settings.gradle.kts b/android/conventions/settings.gradle.kts new file mode 100644 index 000000000..5db6c3eb8 --- /dev/null +++ b/android/conventions/settings.gradle.kts @@ -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. + */ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "conventions" diff --git a/android/conventions/src/main/kotlin/Extensions.kt b/android/conventions/src/main/kotlin/Extensions.kt new file mode 100644 index 000000000..16f216f77 --- /dev/null +++ b/android/conventions/src/main/kotlin/Extensions.kt @@ -0,0 +1,47 @@ +/* + * 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. + */ +import com.android.build.gradle.AbstractAppExtension +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.TestedExtension +import com.android.build.gradle.api.BaseVariant +import net.ltgt.gradle.errorprone.CheckSeverity +import net.ltgt.gradle.errorprone.errorprone +import net.ltgt.gradle.nullaway.nullaway + +fun AbstractAppExtension.errorprone() { + applicationVariants.configureEach { errorprone() } + testErrorprone() +} + +fun LibraryExtension.errorprone() { + libraryVariants.configureEach { errorprone() } + testErrorprone() +} + +fun TestedExtension.testErrorprone() { + testVariants.configureEach { errorprone() } + unitTestVariants.configureEach { errorprone() } +} + +fun BaseVariant.errorprone() { + javaCompileProvider.configure { + options.errorprone.nullaway { + severity.set(CheckSeverity.ERROR) + annotatedPackages.add("com.uber") + } + options.errorprone.excludedPaths.set(".*/build/generated/.*") + } +} diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts new file mode 100644 index 000000000..5b92437da --- /dev/null +++ b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts @@ -0,0 +1,79 @@ +/* + * 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. + */ +@file:Suppress("UnstableApiUsage") + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("android") + id("com.android.application") + id("ribs.spotless-convention") +} + +kotlin { + jvmToolchain(11) +} + +android { + compileSdk = 33 + + defaultConfig { + minSdk = 21 + targetSdk = 28 + versionCode = 1 + versionName = "1.0" + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + // No need for lint. Those are just tutorials. + lint { + abortOnError = false + quiet = true + } + + buildTypes { + debug { + matchingFallbacks.add("release") + } + } + + sourceSets { + getByName("main").java.srcDir("src/main/kotlin") + getByName("test").java.srcDir("src/test/kotlin") + getByName("androidTest").java.srcDir("src/androidTest/kotlin") + } +} + +androidComponents { + beforeVariants { variantBuilder -> + if (variantBuilder.buildType == "release") { + variantBuilder.enable = false + } + } +} + +tasks.withType().configureEach { + compilerOptions { + freeCompilerArgs.addAll( + "-Xjvm-default=enable", + "-opt-in=kotlin.RequiresOptIn", + ) + } +} diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-android-application-errorprone-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-errorprone-conventions.gradle.kts new file mode 100644 index 000000000..a06afdbc6 --- /dev/null +++ b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-errorprone-conventions.gradle.kts @@ -0,0 +1,37 @@ +/* + * 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. + */ +val libs = the() + +plugins { + id("ribs.kotlin-android-application-conventions") + kotlin("kapt") + id("net.ltgt.errorprone") + id("net.ltgt.nullaway") + id("ribs.spotless-convention") +} + +android { + errorprone() +} + +dependencies { + kapt(libs.autodispose.errorprone) + kapt(libs.uber.nullaway) + errorprone(libs.errorprone.core) + errorprone(libs.guava.jre) + errorproneJavac(libs.errorprone.javac) + errorprone(libs.uber.nullaway) +} diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts new file mode 100644 index 000000000..19a2336a2 --- /dev/null +++ b/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts @@ -0,0 +1,76 @@ +/* + * 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. + */ +@file:Suppress("UnstableApiUsage") + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("android") + id("com.android.library") + id("ribs.spotless-convention") +} + +kotlin { + jvmToolchain(11) +} + +android { + compileSdk = 33 + + defaultConfig { + minSdk = 21 + } + + // This can be removed on AGP 8.1.0-alpha09 onwards, since we are using JVM Toolchain + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + buildFeatures { + buildConfig = false + } + + sourceSets { + getByName("main").java.srcDir("src/main/kotlin") + getByName("test").java.srcDir("src/test/kotlin") + getByName("androidTest").java.srcDir("src/androidTest/kotlin") + } + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } +} + +androidComponents { + beforeVariants { variantBuilder -> + if (variantBuilder.buildType == "debug") { + variantBuilder.enable = false + } + } +} + +tasks.withType().configureEach { + compilerOptions { + freeCompilerArgs.addAll( + "-Xexplicit-api=warning", + "-Xjvm-default=enable", + "-opt-in=kotlin.RequiresOptIn", + ) + } +} diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts new file mode 100644 index 000000000..f26c4a37a --- /dev/null +++ b/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts @@ -0,0 +1,30 @@ +/* + * 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. + */ +plugins { + kotlin("jvm") + id("ribs.spotless-convention") +} + +kotlin { + jvmToolchain(11) + explicitApi() +} + +tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask::class.java) { + compilerOptions { + freeCompilerArgs.add("-Xjvm-default=enable") + } +} diff --git a/android/conventions/src/main/kotlin/ribs.spotless-convention.gradle.kts b/android/conventions/src/main/kotlin/ribs.spotless-convention.gradle.kts new file mode 100644 index 000000000..bf1fbe417 --- /dev/null +++ b/android/conventions/src/main/kotlin/ribs.spotless-convention.gradle.kts @@ -0,0 +1,59 @@ +/* + * 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. + */ +plugins { + id("com.diffplug.spotless") +} + +val libs = the() + +configure { + format("misc") { + target("**/*.md", "**/.gitignore") + trimTrailingWhitespace() + endWithNewline() + } + kotlin { + target("**/*.kt") + ktlint(libs.versions.ktlint.get()).editorConfigOverride( + mapOf( + "indent_size" to "2", + "continuation_indent_size" to "4", + ) + ) + ktfmt(libs.versions.ktfmt.get()).googleStyle() + licenseHeaderFile(rootProject.file("config/spotless/copyright.kt")) + trimTrailingWhitespace() + endWithNewline() + } + java { + target("src/*/java/**/*.java") + googleJavaFormat(libs.versions.gjf.get()) + licenseHeaderFile(rootProject.file("config/spotless/copyright.java")) + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + } + kotlinGradle { + target("**/*.gradle.kts") + trimTrailingWhitespace() + endWithNewline() + } + groovyGradle { + target("**/*.gradle") + trimTrailingWhitespace() + endWithNewline() + } +} From 27d8eba3fc93ec350b1736d620977e95752d44df Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Thu, 18 May 2023 07:43:24 -0300 Subject: [PATCH 07/52] Make use of convention plugins and version catalogs --- android/build.gradle | 192 +----------------- android/demos/compose/build.gradle | 73 +++---- android/demos/flipper/build.gradle | 52 ++--- android/demos/intellij/build.gradle | 52 ++--- android/demos/memory-leaks/build.gradle | 72 ++----- android/demos/rib-workers/build.gradle | 74 +++---- android/gradle/dependencies.gradle | 159 --------------- android/gradle/japicmp.gradle | 4 +- .../rib-android-compose/build.gradle | 42 +--- .../libraries/rib-android-core/build.gradle | 40 +--- android/libraries/rib-android/build.gradle | 59 ++---- android/libraries/rib-base/build.gradle | 76 +++---- .../libraries/rib-compiler-app/build.gradle | 25 +-- .../libraries/rib-compiler-test/build.gradle | 26 ++- .../rib-coroutines-test/build.gradle | 38 ++-- android/libraries/rib-coroutines/build.gradle | 36 ++-- .../libraries/rib-debug-utils/build.gradle | 8 +- .../rib-router-navigator/build.gradle | 29 ++- .../rib-screen-stack-base/build.gradle | 38 +--- android/libraries/rib-test/build.gradle | 23 +-- .../libraries/rib-workflow-test/build.gradle | 35 +--- android/libraries/rib-workflow/build.gradle | 42 ++-- android/settings.gradle | 27 +++ .../tooling/rib-flipper-plugin/build.gradle | 53 ++--- .../tooling/rib-intellij-plugin/build.gradle | 48 ++--- .../intellij-broadcast-rib/build.gradle | 53 ++--- .../intellij-broadcast-core/build.gradle | 52 ++--- android/tutorials/tutorial1/build.gradle | 55 +---- android/tutorials/tutorial2/build.gradle | 61 ++---- .../tutorial3-completed/build.gradle | 52 ++--- android/tutorials/tutorial3/build.gradle | 50 ++--- android/tutorials/tutorial4/build.gradle | 60 ++---- 32 files changed, 446 insertions(+), 1260 deletions(-) delete mode 100755 android/gradle/dependencies.gradle diff --git a/android/build.gradle b/android/build.gradle index 5eb294935..58a747b81 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -13,186 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import net.ltgt.gradle.errorprone.CheckSeverity -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - - - -buildscript { - apply from: rootProject.file('gradle/dependencies.gradle') - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.kotlin - classpath deps.build.gradlePlugins.spotless - classpath deps.build.gradlePlugins.gradleMavenPublish - } +plugins { + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.androidLibrary) apply false + alias(libs.plugins.kotlinAndroid) apply false + alias(libs.plugins.kotlinKapt) apply false + alias(libs.plugins.mavenPublish) apply false + alias(libs.plugins.errorprone) apply false + alias(libs.plugins.nullaway) apply false + alias(libs.plugins.intellij) apply false + alias(libs.plugins.spotless) apply false } - -Set useErrorProneProjects = [ - "memory-leaks", - "tutorial1", - "tutorial2" -] - -def moduleFriends = [ - 'rib-android': 'rib-base', - 'rib-debug-utils': 'rib-base', - 'rib-router-navigator': 'rib-base', - 'rib-test': 'rib-base', - 'rib-workflow-test': 'rib-workflow', - 'rib-coroutines-test': 'rib-coroutines' -] - -subprojects { - buildscript { - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - } - - repositories { - google() - mavenCentral() - } - - apply plugin: 'com.diffplug.spotless' - spotless { - format 'misc', { - target '**/*.md', '**/.gitignore' - - trimTrailingWhitespace() - endWithNewline() - } - kotlin { - target "**/*.kt" - ktlint(deps.versions.ktlint).editorConfigOverride(['indent_size': '2', 'continuation_indent_size' : '4']) - ktfmt(deps.versions.ktfmt).googleStyle() - licenseHeaderFile rootProject.file('config/spotless/copyright.kt') - trimTrailingWhitespace() - endWithNewline() - } - java { - target "src/*/java/**/*.java" - googleJavaFormat(deps.versions.gjf) - licenseHeaderFile rootProject.file('config/spotless/copyright.java') - removeUnusedImports() - trimTrailingWhitespace() - endWithNewline() - } - groovyGradle { - target '**/*.gradle' - trimTrailingWhitespace() - endWithNewline() - } - } - - afterEvaluate { - boolean isAndroidApp = project.plugins.hasPlugin("com.android.application") - boolean isAndroidLibrary = project.plugins.hasPlugin("com.android.library") - boolean isIntelliJPlugin = project.plugins.hasPlugin("com.android.library") - boolean usesErrorProne = project.name in useErrorProneProjects - boolean isKotlinLibrary = project.plugins.hasPlugin("org.jetbrains.kotlin.jvm") || project.plugins.hasPlugin("org.jetbrains.kotlin.android") - - if ((isAndroidLibrary || isAndroidApp) && usesErrorProne) { - def configurer = { variant -> - variant.getJavaCompileProvider().configure { - options.errorprone.nullaway { - severity = CheckSeverity.ERROR - annotatedPackages.add("com.uber") - } - options.errorprone.excludedPaths = ".*/build/generated/.*" - } - } - if (isAndroidLibrary) { - project.android.libraryVariants.configureEach(configurer) - } - if (isAndroidApp) { - project.android.applicationVariants.configureEach(configurer) - } - project.android.testVariants.configureEach(configurer) - project.android.unitTestVariants.configureEach(configurer) - } - if (isAndroidLibrary || isAndroidApp) { - // TODO replace with https://issuetracker.google.com/issues/72050365 once released. - project.android { - if (isAndroidLibrary) { - variantFilter { variant -> - if (variant.buildType.name == 'debug') { - variant.setIgnore(true) - } - } - } - if (isAndroidApp) { - buildTypes { - debug { - matchingFallbacks = ['release'] - } - } - variantFilter { variant -> - if (variant.buildType.name == "release") { - variant.setIgnore(true) - } - } - } - } - } - - if (isKotlinLibrary) { - def extraCompilerArgs = [ - // See https://github.com/bazelbuild/bazel/issues/15144 - "-Xjvm-default=enable", - "-opt-in=kotlin.RequiresOptIn", - ] - if (project.name in moduleFriends.keySet()) { - def friendName = moduleFriends[project.name] - def friendProject = rootProject.subprojects.stream().filter { it.name == friendName }.findFirst().get() - def outputJarPath = friendProject.plugins.hasPlugin("com.android.library") - ? "build/intermediates/compile_library_classes_jar/release/classes.jar" - : "build/libs/$friendName-${project.property('VERSION_NAME')}.jar" - def friendPath="${project.rootDir}/libraries/${moduleFriends[project.name]}/$outputJarPath" - extraCompilerArgs.add("-Xfriend-paths=$friendPath") - } - if (isAndroidLibrary || isAndroidApp) { - project.android.sourceSets { - main.java.srcDirs += 'src/main/kotlin' - test.java.srcDirs += 'src/test/kotlin' - androidTest.java.srcDirs += 'src/androidTest/kotlin' - } - - tasks.withType(KotlinCompile).configureEach { - kotlinOptions { - freeCompilerArgs = extraCompilerArgs - jvmTarget = "11" - } - } - } else if(!isIntelliJPlugin) { - project.compileKotlin { - kotlinOptions { - freeCompilerArgs = extraCompilerArgs - jvmTarget = "11" - } - } - project.compileTestKotlin { - kotlinOptions { - freeCompilerArgs = extraCompilerArgs - jvmTarget = "11" - } - } - } - } - } -} - -tasks.register('clean', Delete) { - delete rootProject.buildDir -} - -apply from: rootProject.file('gradle/japicmp.gradle') diff --git a/android/demos/compose/build.gradle b/android/demos/compose/build.gradle index 2b8d1cf83..0b7c75e54 100644 --- a/android/demos/compose/build.gradle +++ b/android/demos/compose/build.gradle @@ -1,63 +1,44 @@ -apply plugin: 'com.android.application' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "org.jetbrains.kotlin.kapt" - -kotlin { - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.compose" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.rib.compose" - versionCode 1 - versionName "1.0" } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion deps.versions.androidx.compose.compiler - } - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true + kotlinCompilerExtensionVersion libs.versions.compose.compiler.get() } } dependencies { - kapt deps.uber.motifCompiler - implementation project(":libraries:rib-android") - implementation project(":libraries:rib-android-compose") - implementation project(":libraries:rib-coroutines") - implementation deps.androidx.activityCompose - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.androidx.composeAnimation - implementation deps.androidx.composeFoundation - implementation deps.androidx.composeMaterial - implementation deps.androidx.composeNavigation - implementation deps.androidx.composeRuntimeRxJava2 - implementation deps.androidx.composeUi - implementation deps.androidx.composeViewModel - implementation deps.androidx.composeUiTooling - implementation deps.androidx.savedState - implementation deps.external.rxandroid2 - implementation deps.kotlin.coroutines - implementation deps.kotlin.coroutinesAndroid - implementation deps.kotlin.coroutinesRx2 - implementation deps.uber.autodisposeCoroutines - implementation deps.uber.motif + kapt(libs.motif.compiler) + implementation(project(":libraries:rib-android")) + implementation(project(":libraries:rib-android-compose")) + implementation(project(":libraries:rib-coroutines")) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.activity.compose) + implementation(libs.compose.animation) + implementation(libs.compose.foundation) + implementation(libs.compose.material) + implementation(libs.compose.navigation) + implementation(libs.compose.runtime.rx2) + implementation(libs.compose.ui) + implementation(libs.compose.viewmodel) + implementation(libs.compose.uitooling) + implementation(libs.savedstate) + implementation(libs.rxandroid2) + implementation(libs.coroutines.android) + implementation(libs.coroutines.rx2) + implementation(libs.autodispose.coroutines) + implementation(libs.motif.library) // Flipper Debug tool integration @@ -66,7 +47,7 @@ dependencies { releaseImplementation 'com.facebook.flipper:flipper-noop:0.93.0' // Flipper RIBs plugin - implementation project(":tooling:rib-flipper-plugin") + implementation(project(":tooling:rib-flipper-plugin")) - testImplementation deps.test.junit + testImplementation(testLibs.junit) } diff --git a/android/demos/flipper/build.gradle b/android/demos/flipper/build.gradle index 8d957e0db..efe0727be 100644 --- a/android/demos/flipper/build.gradle +++ b/android/demos/flipper/build.gradle @@ -13,55 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.flipper" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.rib.flipper" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a demo. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.androidx.percent - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.percent) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) // Flipper Debug tool integration debugImplementation 'com.facebook.flipper:flipper:0.93.0' @@ -69,5 +43,5 @@ dependencies { releaseImplementation 'com.facebook.flipper:flipper-noop:0.93.0' // Flipper RIBs plugin - implementation project(":tooling:rib-flipper-plugin") + implementation(project(":tooling:rib-flipper-plugin")) } diff --git a/android/demos/intellij/build.gradle b/android/demos/intellij/build.gradle index 083d2e8c6..52110171f 100644 --- a/android/demos/intellij/build.gradle +++ b/android/demos/intellij/build.gradle @@ -13,56 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.intellij" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.rib.intellij" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a demo. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.androidx.percent - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.percent) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) // IntelliJ debugging integration - implementation project(":tooling:rib-intellij-plugin:native:intellij-broadcast-rib") + implementation(project(":tooling:rib-intellij-plugin:native:intellij-broadcast-rib")) } diff --git a/android/demos/memory-leaks/build.gradle b/android/demos/memory-leaks/build.gradle index 97f3764d3..6a83737f1 100644 --- a/android/demos/memory-leaks/build.gradle +++ b/android/demos/memory-leaks/build.gradle @@ -13,73 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - -buildscript { - repositories { - google() - mavenCentral() - gradlePluginPortal() - } - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.nullaway - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'net.ltgt.errorprone' -apply plugin: 'net.ltgt.nullaway' - android { namespace "com.uber.rib.memory_leak" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial3" - versionCode 1 - versionName "1.0" - } - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true } } dependencies { - annotationProcessor deps.uber.autodisposeErrorProne - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-app") - annotationProcessor project(":libraries:rib-compiler-test") - annotationProcessor deps.apt.nullAway - implementation project(":libraries:rib-android") - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.external.leakcanaryDebug - implementation deps.androidx.percent - implementation deps.androidx.appcompat - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") - errorprone deps.build.errorProne - errorprone deps.build.guavaJre - errorproneJavac deps.build.errorProneJavac - errorprone deps.build.nullAway + implementation(project(":libraries:rib-android")) + kapt(project(":libraries:rib-compiler-app")) + kapt(project(":libraries:rib-compiler-test")) + kapt(libs.autodispose.errorprone) + kapt(libs.dagger.compiler) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.leakcanary) + implementation(libs.percent) + implementation(libs.appcompat) + compileOnly(libs.jsr250) + + testImplementation(project(":libraries:rib-test")) } tasks.withType(JavaCompile).configureEach { diff --git a/android/demos/rib-workers/build.gradle b/android/demos/rib-workers/build.gradle index 86cfa6a9d..46eefe2cc 100644 --- a/android/demos/rib-workers/build.gradle +++ b/android/demos/rib-workers/build.gradle @@ -1,66 +1,48 @@ -apply plugin: 'com.android.application' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "org.jetbrains.kotlin.kapt" - -kotlin { - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") } android { - namespace "com.uber.rib.workers" - compileSdk deps.build.compileSdkVersion + namespace = "com.uber.rib.workers" defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion - applicationId "com.uber.rib.workers" - versionCode 1 - versionName "1.0" + applicationId = "com.uber.workers" } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion deps.versions.androidx.compose.compiler - } - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - lint { - abortOnError false - quiet true + kotlinCompilerExtensionVersion libs.versions.compose.compiler.get() } } dependencies { - kapt deps.uber.motifCompiler - implementation project(":libraries:rib-android") - implementation project(":libraries:rib-android-compose") - implementation project(":libraries:rib-coroutines") - implementation deps.androidx.activityCompose - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.androidx.composeAnimation - implementation deps.androidx.composeFoundation - implementation deps.androidx.composeMaterial - implementation deps.androidx.composeNavigation - implementation deps.androidx.composeRuntimeRxJava2 - implementation deps.androidx.composeUi - implementation deps.androidx.composeViewModel - implementation deps.androidx.composeUiTooling - implementation deps.androidx.savedState - implementation deps.external.rxandroid2 - implementation deps.kotlin.coroutines - implementation deps.kotlin.coroutinesAndroid - implementation deps.kotlin.coroutinesRx2 - implementation deps.uber.autodisposeCoroutines - implementation deps.uber.motif + kapt(libs.motif.compiler) + implementation(project(":libraries:rib-android")) + implementation(project(":libraries:rib-android-compose")) + implementation(project(":libraries:rib-coroutines")) + implementation(libs.activity.compose) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.compose.animation) + implementation(libs.compose.foundation) + implementation(libs.compose.material) + implementation(libs.compose.navigation) + implementation(libs.compose.runtime.rx2) + implementation(libs.compose.ui) + implementation(libs.compose.viewmodel) + implementation(libs.compose.uitooling) + implementation(libs.savedstate) + implementation(libs.rxandroid2) + implementation(libs.coroutines.core) + implementation(libs.coroutines.android) + implementation(libs.coroutines.rx2) + implementation(libs.autodispose.coroutines) + implementation(libs.motif.library) debugImplementation 'com.facebook.flipper:flipper:0.93.0' debugImplementation 'com.facebook.soloader:soloader:0.10.1' releaseImplementation 'com.facebook.flipper:flipper-noop:0.93.0' - implementation project(":tooling:rib-flipper-plugin") + implementation(project(":tooling:rib-flipper-plugin")) } diff --git a/android/gradle/dependencies.gradle b/android/gradle/dependencies.gradle deleted file mode 100755 index 3c0bb1df8..000000000 --- a/android/gradle/dependencies.gradle +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2017. 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. - */ - -def versions = [ - androidx: [ - activity: '1.7.0', - annotations: '1.1.0', - appcompat: '1.3.0', - compose: [ - compiler: "1.4.6", - libraries: "1.4.0" - ], - lifecycle: '2.5.1', - percent: '1.0.0', - savedState: "1.2.0" - ], - autodispose: '1.4.0', - coroutines: '1.6.4', - dagger: "2.43.2", - errorProne: '2.3.3', - gjf: '1.16.0', - intellij: "2022.2.4", - kotlin: "1.8.20", - ktfmt: '0.43', - ktlint: '0.48.2', - motif: '0.3.4', - robolectric: "4.4", - spotless: '6.18.0' -] - -def apt = [ - androidApi: "com.google.android:android:2.2.1", - autoCommon: "com.google.auto:auto-common:0.8", - autoService: "com.google.auto.service:auto-service:1.0-rc4", - nullAway: 'com.uber.nullaway:nullaway:0.9.0', - daggerCompiler: "com.google.dagger:dagger-compiler:${versions.dagger}", - javapoet: "com.squareup:javapoet:1.11.1", - javax: "javax.annotation:jsr250-api:1.0", - javaxInject: "javax.inject:javax.inject:1", - autoValue: "com.google.auto.value:auto-value:1.7", - autoValueAnnotations: "com.google.auto.value:auto-value-annotations:1.7", - errorProneAnnotations: "com.google.errorprone:error_prone_annotations:${versions.errorProne}", -] - -def build = [ - buildToolsVersion: '33.0.0', - compileSdkVersion: 33, - ci: 'true' == System.getenv('CI'), - minSdkVersion: 21, - targetSdkVersion: 28, - javaVersion: JavaVersion.VERSION_11, - guavaJre: "com.google.guava:guava:27.1-jre", - commonsLang: "commons-lang:commons-lang:2.6", - intellijPlugin: "org.jetbrains.intellij.plugins:gradle-intellij-plugin:1.13.1", - errorProne: "com.google.errorprone:error_prone_core:${versions.errorProne}", - errorProneJavac: "com.google.errorprone:javac:9+181-r4173-1", - errorProneCore: "com.google.errorprone:error_prone_core:${versions.errorProne}", - errorProneTestHelpers: "com.google.errorprone:error_prone_test_helpers:${versions.errorProne}", - nullAway: 'com.uber.nullaway:nullaway:0.9.0', - gradlePlugins: [ - android: 'com.android.tools.build:gradle:7.4.2', - apt: "net.ltgt.gradle:gradle-apt-plugin:0.21", - errorprone: "net.ltgt.gradle:gradle-errorprone-plugin:1.3.0", - gradleMavenPublish: "com.vanniktech:gradle-maven-publish-plugin:0.25.2", - japicmp: 'me.champeau.gradle:japicmp-gradle-plugin:0.2.8', - kapt: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", - kotlin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", - nullaway: "net.ltgt.gradle:gradle-nullaway-plugin:1.0.2", - spotless: "com.diffplug.spotless:spotless-plugin-gradle:${versions.spotless}" - ] -] - -def androidx = [ - activity: "androidx.activity:activity:${versions.androidx.activity}", - activityCompose: "androidx.activity:activity-compose:${versions.androidx.activity}", - activityKtx: "androidx.activity:activity-ktx:${versions.androidx.activity}", - annotations: "androidx.annotation:annotation:${versions.androidx.annotations}", - appcompat: "androidx.appcompat:appcompat:${versions.androidx.appcompat}", - composeAnimation: "androidx.compose.animation:animation:${versions.androidx.compose.libraries}", - composeCompiler: "androidx.compose.compiler:compiler:${versions.androidx.compose.compiler}", - composeFoundation: "androidx.compose.foundation:foundation:${versions.androidx.compose.libraries}", - composeMaterial: "androidx.compose.material:material:${versions.androidx.compose.libraries}", - composeNavigation: "androidx.navigation:navigation-compose:2.4.0-alpha03", - composeRuntimeRxJava2: "androidx.compose.runtime:runtime-rxjava2:${versions.androidx.compose.libraries}", - composeUi: "androidx.compose.ui:ui:${versions.androidx.compose.libraries}", - composeUiTooling: "androidx.compose.ui:ui-tooling:${versions.androidx.compose.libraries}", - composeViewModel: "androidx.lifecycle:lifecycle-viewmodel-compose:${versions.androidx.lifecycle}", - percent: "androidx.percentlayout:percentlayout:${versions.androidx.percent}", - savedState: "androidx.savedstate:savedstate:${versions.androidx.savedState}" -] - -def test = [ - junit: "junit:junit:4.12", - mockito: "org.mockito:mockito-core:4.6.1", - mockitoKotlin: "org.mockito.kotlin:mockito-kotlin:4.0.0", - compileTesting: "com.google.testing.compile:compile-testing:0.17", - truth: "com.google.truth:truth:0.43", -] - -def external = [ - android: "com.google.android:android:4.1.1.4", - checkerQual: "org.checkerframework:checker-qual:2.5.1", - dagger: "com.google.dagger:dagger:${versions.dagger}", - ddms: "com.android.tools.ddms:ddmlib:27.1.3", - guavaAndroid: "com.google.guava:guava:27.1-android", - gson: "com.google.code.gson:gson:2.8.7", - flipper: "com.facebook.flipper:flipper:0.93.0", - rxjava2: "io.reactivex.rxjava2:rxjava:2.2.8", - rxrelay2: "com.jakewharton.rxrelay2:rxrelay:2.1.0", - rxandroid2: "io.reactivex.rxjava2:rxandroid:2.1.1", - reactiveStreams: "org.reactivestreams:reactive-streams:1.0.0", - roboelectricBase: "org.robolectric:robolectric:${versions.robolectric}", - rxbinding: 'com.jakewharton.rxbinding2:rxbinding:2.0.0', - rxkotlin: 'io.reactivex.rxjava2:rxkotlin:2.2.0', - leakcanaryDebug: 'com.squareup.leakcanary:leakcanary-android:1.5.4', - -] - -def kotlin = [ - coroutines: "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutines}", - coroutinesAndroid: "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutines}", - coroutinesRx2: "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:${versions.coroutines}", - coroutinesTest: "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.coroutines}", - stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}" -] - -def uber = [ - autodispose: "com.uber.autodispose:autodispose:${versions.autodispose}", - autodisposeAndroid : "com.uber.autodispose:autodispose-android:${versions.autodispose}@aar", - autodisposeLifecycle: "com.uber.autodispose:autodispose-lifecycle:${versions.autodispose}", - autodisposeCoroutines: "com.uber.autodispose:autodispose-coroutines-interop:${versions.autodispose}", - autodisposeErrorProne: "com.uber.autodispose:autodispose-error-prone:${versions.autodispose}", - motif: "com.uber.motif:motif:${versions.motif}", - motifCompiler: "com.uber.motif:motif-compiler:${versions.motif}", -] - -ext.deps = [ - "androidx": androidx, - "apt": apt, - "build": build, - "external": external, - "kotlin": kotlin, - "test": test, - "uber": uber, - "versions": versions -] diff --git a/android/gradle/japicmp.gradle b/android/gradle/japicmp.gradle index 65d10a827..d0f3810e6 100644 --- a/android/gradle/japicmp.gradle +++ b/android/gradle/japicmp.gradle @@ -7,8 +7,8 @@ buildscript { gradlePluginPortal() } dependencies { - classpath deps.build.gradlePlugins.japicmp - classpath deps.build.guavaJre // https://github.com/melix/japicmp-gradle-plugin/issues/36 + classpath "me.champeau.gradle:japicmp-gradle-plugin:0.2.8" + classpath libs.guava.jre // https://github.com/melix/japicmp-gradle-plugin/issues/36 } } diff --git a/android/libraries/rib-android-compose/build.gradle b/android/libraries/rib-android-compose/build.gradle index 420d79ad9..c923b5c1d 100644 --- a/android/libraries/rib-android-compose/build.gradle +++ b/android/libraries/rib-android-compose/build.gradle @@ -13,48 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.android.compose" - compileSdk deps.build.compileSdkVersion - defaultConfig { - minSdk deps.build.minSdkVersion - } buildFeatures { compose true - buildConfig false - } - composeOptions { - kotlinCompilerExtensionVersion deps.versions.androidx.compose.compiler - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } - testOptions { - unitTests { - includeAndroidResources = true - } + composeOptions { + kotlinCompilerExtensionVersion libs.versions.compose.compiler.get() } } dependencies { - api project(":libraries:rib-android") - implementation deps.androidx.composeFoundation - implementation deps.androidx.composeUi - testImplementation deps.external.roboelectricBase - testImplementation deps.test.mockitoKotlin - testImplementation project(":libraries:rib-test") + api(project(":libraries:rib-android")) + implementation(libs.compose.foundation) + implementation(libs.compose.ui) + testImplementation(testLibs.robolectric) + testImplementation(testLibs.mockitoKotlin) + testImplementation(project(":libraries:rib-test")) } diff --git a/android/libraries/rib-android-core/build.gradle b/android/libraries/rib-android-core/build.gradle index bd39b8e09..2e4cabff9 100644 --- a/android/libraries/rib-android-core/build.gradle +++ b/android/libraries/rib-android-core/build.gradle @@ -14,43 +14,19 @@ * limitations under the License. */ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.android.core" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } } dependencies { - implementation deps.apt.javaxInject - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - testImplementation deps.androidx.appcompat - testImplementation deps.external.roboelectricBase + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + testImplementation(libs.appcompat) + testImplementation(testLibs.robolectric) } diff --git a/android/libraries/rib-android/build.gradle b/android/libraries/rib-android/build.gradle index 378f27102..4c6406eea 100644 --- a/android/libraries/rib-android/build.gradle +++ b/android/libraries/rib-android/build.gradle @@ -13,51 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: 'com.vanniktech.maven.publish' - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.android" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } } dependencies { - api project(":libraries:rib-android-core") - api project(":libraries:rib-base") - api deps.external.rxkotlin - api deps.external.rxrelay2 - api deps.external.rxjava2 - implementation deps.apt.javaxInject - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.external.guavaAndroid - implementation deps.uber.autodisposeCoroutines - implementation deps.kotlin.coroutinesAndroid - implementation deps.kotlin.coroutinesRx2 - testImplementation deps.external.roboelectricBase - testImplementation deps.androidx.appcompat - testImplementation deps.test.mockitoKotlin - testImplementation project(":libraries:rib-test") + api(project(":libraries:rib-android-core")) + api(project(":libraries:rib-base")) + api(libs.rxkotlin) + api(libs.rxrelay2) + api(libs.rxjava2) + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.guava.android) + implementation(libs.autodispose.coroutines) + implementation(libs.coroutines.android) + implementation(libs.coroutines.rx2) + testImplementation(testLibs.robolectric) + testImplementation(libs.appcompat) + testImplementation(testLibs.mockitoKotlin) + testImplementation(project(":libraries:rib-test")) } diff --git a/android/libraries/rib-base/build.gradle b/android/libraries/rib-base/build.gradle index 5b2051290..fff35d78c 100644 --- a/android/libraries/rib-base/build.gradle +++ b/android/libraries/rib-base/build.gradle @@ -13,54 +13,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.apt - } -} - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "org.jetbrains.kotlin.kapt" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.kotlinKapt) + alias(libs.plugins.mavenPublish) } dependencies { // RIBs themselves don't need to use dagger. But the base library does use dagger // in order to invert a dependency. With a bit of work this could be removed. - kapt deps.apt.daggerCompiler - kapt deps.apt.androidApi - - implementation deps.external.guavaAndroid - implementation deps.external.reactiveStreams - implementation deps.external.rxrelay2 - implementation deps.external.rxjava2 - implementation deps.uber.autodispose - api deps.uber.autodisposeLifecycle - implementation deps.apt.javaxInject - - implementation deps.uber.autodisposeCoroutines - implementation deps.kotlin.coroutinesRx2 - api deps.kotlin.stdlib - api deps.kotlin.coroutines - api project(":libraries:rib-coroutines") - - compileOnly deps.apt.daggerCompiler - compileOnly deps.androidx.annotations - compileOnly deps.apt.androidApi - compileOnly deps.external.checkerQual - - testImplementation project(":libraries:rib-coroutines-test") - testImplementation deps.androidx.annotations - testImplementation deps.apt.androidApi - testImplementation deps.test.junit - testImplementation deps.test.mockito - testImplementation deps.test.mockitoKotlin - testImplementation deps.test.truth + kapt(libs.dagger.compiler) + kapt(libs.android.api) + + implementation(libs.guava.android) + implementation(libs.reactivestreams) + implementation(libs.rxrelay2) + implementation(libs.rxjava2) + implementation(libs.autodispose.library) + api(libs.autodispose.lifecycle) + implementation(libs.javax.inject) + + implementation(libs.autodispose.coroutines) + implementation(libs.coroutines.rx2) + api(libs.kotlin.stdlib) + api(libs.coroutines.core) + api(project(":libraries:rib-coroutines")) + + compileOnly(libs.dagger.compiler) + compileOnly(libs.annotation) + compileOnly(libs.android.api) + compileOnly(libs.checkerqual) + + testImplementation(project(":libraries:rib-coroutines-test")) + testImplementation(libs.annotation) + testImplementation(libs.android.api) + testImplementation(testLibs.junit) + testImplementation(testLibs.mockito) + testImplementation(testLibs.mockitoKotlin) + testImplementation(testLibs.truth) testImplementation(project(":libraries:rib-test")) { transitive = false } diff --git a/android/libraries/rib-compiler-app/build.gradle b/android/libraries/rib-compiler-app/build.gradle index 4280f7227..6da04f79a 100644 --- a/android/libraries/rib-compiler-app/build.gradle +++ b/android/libraries/rib-compiler-app/build.gradle @@ -14,26 +14,23 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } dependencies { - implementation project(":libraries:rib-base") + implementation(project(":libraries:rib-base")) - implementation deps.apt.autoCommon - implementation deps.apt.javapoet - implementation deps.external.dagger + implementation(libs.autocommon) + implementation(libs.javapoet) + implementation(libs.dagger.library) - compileOnly deps.androidx.annotations - compileOnly deps.apt.autoService - compileOnly deps.apt.androidApi + compileOnly(libs.annotation) + compileOnly(libs.autoservice) + compileOnly(libs.android.api) - testImplementation deps.test.compileTesting + testImplementation(testLibs.compileTesting) } // https://code.google.com/p/android/issues/detail?id=64887 diff --git a/android/libraries/rib-compiler-test/build.gradle b/android/libraries/rib-compiler-test/build.gradle index ff3a8913c..33eff4ef0 100644 --- a/android/libraries/rib-compiler-test/build.gradle +++ b/android/libraries/rib-compiler-test/build.gradle @@ -16,25 +16,23 @@ import org.gradle.internal.jvm.Jvm * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "org.jetbrains.kotlin.kapt" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.kotlinKapt) + alias(libs.plugins.mavenPublish) } dependencies { - implementation project(":libraries:rib-compiler-app") - implementation deps.apt.javapoet + implementation(project(":libraries:rib-compiler-app")) + implementation(libs.javapoet) - compileOnly deps.androidx.annotations - compileOnly deps.apt.autoService - compileOnly deps.apt.androidApi - kapt deps.apt.autoService + compileOnly(libs.annotation) + compileOnly(libs.autoservice) + compileOnly(libs.android.api) + kapt(libs.autoservice) - testImplementation deps.androidx.annotations - testImplementation deps.test.compileTesting + testImplementation(libs.annotation) + testImplementation(testLibs.compileTesting) if (!Jvm.current().javaVersion.isJava9Compatible()) { testCompile files(Jvm.current().getToolsJar()) } diff --git a/android/libraries/rib-coroutines-test/build.gradle b/android/libraries/rib-coroutines-test/build.gradle index fa0d1c4a3..ae4e3b01a 100644 --- a/android/libraries/rib-coroutines-test/build.gradle +++ b/android/libraries/rib-coroutines-test/build.gradle @@ -14,31 +14,27 @@ * limitations under the License. */ - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } dependencies { - api project(':libraries:rib-coroutines') - api deps.kotlin.coroutinesTest - api deps.test.junit + api(project(':libraries:rib-coroutines')) + api(testLibs.coroutines.test) + api(testLibs.junit) - compileOnly deps.external.android + compileOnly(libs.android.api) - testImplementation project(":libraries:rib-base") - testImplementation project(":libraries:rib-test") - testImplementation deps.test.junit - testImplementation deps.test.mockito - testImplementation deps.test.mockitoKotlin - testImplementation deps.test.truth - testImplementation deps.kotlin.coroutinesTest - testImplementation deps.kotlin.coroutinesAndroid - testImplementation deps.androidx.annotations - testImplementation deps.apt.androidApi + testImplementation(project(":libraries:rib-base")) + testImplementation(project(":libraries:rib-test")) + testImplementation(testLibs.junit) + testImplementation(testLibs.mockito) + testImplementation(testLibs.mockitoKotlin) + testImplementation(testLibs.truth) + testImplementation(testLibs.coroutines.test) + testImplementation(libs.coroutines.android) + testImplementation(libs.annotation) + testImplementation(libs.android.api) } diff --git a/android/libraries/rib-coroutines/build.gradle b/android/libraries/rib-coroutines/build.gradle index 6772e817f..a1c457171 100644 --- a/android/libraries/rib-coroutines/build.gradle +++ b/android/libraries/rib-coroutines/build.gradle @@ -14,30 +14,26 @@ * limitations under the License. */ - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } dependencies { - api deps.uber.autodisposeCoroutines - api deps.kotlin.coroutinesAndroid - api deps.kotlin.coroutinesRx2 + api(libs.autodispose.coroutines) + api(libs.coroutines.android) + api(libs.coroutines.rx2) - compileOnly deps.external.android + compileOnly(libs.android.api) - testImplementation project(":libraries:rib-base") - testImplementation project(":libraries:rib-test") - testImplementation deps.test.junit - testImplementation deps.test.mockito - testImplementation deps.test.mockitoKotlin - testImplementation deps.test.truth - testImplementation deps.kotlin.coroutinesTest - testImplementation deps.androidx.annotations - testImplementation deps.apt.androidApi + testImplementation(project(":libraries:rib-base")) + testImplementation(project(":libraries:rib-test")) + testImplementation(testLibs.junit) + testImplementation(testLibs.mockito) + testImplementation(testLibs.mockitoKotlin) + testImplementation(testLibs.truth) + testImplementation(testLibs.coroutines.test) + testImplementation(libs.annotation) + testImplementation(libs.android.api) } diff --git a/android/libraries/rib-debug-utils/build.gradle b/android/libraries/rib-debug-utils/build.gradle index 9176d5013..f7f15783e 100644 --- a/android/libraries/rib-debug-utils/build.gradle +++ b/android/libraries/rib-debug-utils/build.gradle @@ -14,8 +14,10 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) +} kotlin { explicitApi() @@ -23,5 +25,5 @@ kotlin { } dependencies { - implementation project(":libraries:rib-base") + implementation(project(":libraries:rib-base")) } diff --git a/android/libraries/rib-router-navigator/build.gradle b/android/libraries/rib-router-navigator/build.gradle index 41dd11678..2f2adca0d 100644 --- a/android/libraries/rib-router-navigator/build.gradle +++ b/android/libraries/rib-router-navigator/build.gradle @@ -14,24 +14,21 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } dependencies { - implementation deps.external.checkerQual - implementation deps.uber.autodisposeCoroutines - implementation deps.kotlin.coroutinesAndroid - implementation deps.kotlin.coroutinesRx2 - implementation project(':libraries:rib-base') - compileOnly deps.androidx.annotations - compileOnly deps.apt.androidApi + implementation(libs.checkerqual) + implementation(libs.autodispose.coroutines) + implementation(libs.coroutines.android) + implementation(libs.coroutines.rx2) + implementation(project(':libraries:rib-base')) + compileOnly(libs.annotation) + compileOnly(libs.android.api) - testImplementation deps.test.junit - testImplementation deps.test.mockitoKotlin - testImplementation deps.test.truth + testImplementation(testLibs.junit) + testImplementation(testLibs.mockitoKotlin) + testImplementation(testLibs.truth) } diff --git a/android/libraries/rib-screen-stack-base/build.gradle b/android/libraries/rib-screen-stack-base/build.gradle index 04786e7f4..4641140e9 100644 --- a/android/libraries/rib-screen-stack-base/build.gradle +++ b/android/libraries/rib-screen-stack-base/build.gradle @@ -14,39 +14,21 @@ * limitations under the License. */ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.ubercab.core.screenstack.base" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } } dependencies { - api deps.external.rxjava2 - api deps.external.rxrelay2 - api deps.external.rxbinding - implementation deps.androidx.annotations - implementation deps.uber.autodisposeCoroutines - implementation deps.kotlin.coroutinesAndroid - implementation deps.kotlin.coroutinesRx2 + api(libs.rxjava2) + api(libs.rxrelay2) + api(libs.rxbinding) + implementation(libs.annotation) + implementation(libs.autodispose.coroutines) + implementation(libs.coroutines.android) + implementation(libs.coroutines.rx2) } diff --git a/android/libraries/rib-test/build.gradle b/android/libraries/rib-test/build.gradle index 4f0921003..2b1cf8981 100644 --- a/android/libraries/rib-test/build.gradle +++ b/android/libraries/rib-test/build.gradle @@ -14,20 +14,17 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } dependencies { - api project(":libraries:rib-base") - implementation deps.external.rxjava2 - implementation deps.kotlin.stdlib - api deps.test.junit - api deps.test.truth - api deps.test.mockito - implementation deps.test.mockitoKotlin + api(project(":libraries:rib-base")) + implementation(libs.rxjava2) + implementation(libs.kotlin.stdlib) + api(testLibs.junit) + api(testLibs.truth) + api(testLibs.mockito) + implementation(testLibs.mockitoKotlin) } diff --git a/android/libraries/rib-workflow-test/build.gradle b/android/libraries/rib-workflow-test/build.gradle index 577534ea4..0e2aebb8f 100644 --- a/android/libraries/rib-workflow-test/build.gradle +++ b/android/libraries/rib-workflow-test/build.gradle @@ -13,32 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.workflow.test" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } // 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. @@ -49,9 +30,9 @@ android { } dependencies { - api project(":libraries:rib-workflow") - api deps.external.guavaAndroid - api deps.external.rxjava2 - implementation deps.androidx.annotations - implementation deps.test.truth + api(project(":libraries:rib-workflow")) + api(libs.guava.android) + api(libs.rxjava2) + implementation(libs.annotation) + implementation(testLibs.truth) } diff --git a/android/libraries/rib-workflow/build.gradle b/android/libraries/rib-workflow/build.gradle index 97a3b5f22..e0b5910c2 100644 --- a/android/libraries/rib-workflow/build.gradle +++ b/android/libraries/rib-workflow/build.gradle @@ -14,41 +14,23 @@ * limitations under the License. */ -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.workflow" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } } dependencies { - implementation deps.androidx.annotations - implementation deps.external.rxandroid2 - implementation deps.external.rxkotlin - api deps.external.guavaAndroid - api deps.external.rxjava2 - api project(":libraries:rib-android") - testImplementation deps.test.junit - testImplementation deps.test.truth - testImplementation deps.test.mockito + implementation(libs.annotation) + implementation(libs.rxandroid2) + implementation(libs.rxkotlin) + api(libs.guava.android) + api(libs.rxjava2) + api(project(":libraries:rib-android")) + testImplementation(testLibs.junit) + testImplementation(testLibs.truth) + testImplementation(testLibs.mockito) } diff --git a/android/settings.gradle b/android/settings.gradle index c3c581cf6..42a33dcc6 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,3 +1,30 @@ +import org.gradle.api.initialization.resolve.RepositoriesMode + +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} + +dependencyResolutionManagement { + // rib-intellij-plugin project applies the IntelliJ plugin, which applies custom repositories. + // Otherwise, this should be FAIL_ON_PROJECT_REPO. + repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) + repositories { + google() + mavenCentral() + } + versionCatalogs { + testLibs { + from(files("gradle/test-libs.versions.toml")) + } + } +} + +includeBuild 'conventions' + include ':libraries:rib-test' include ':libraries:rib-android' include ':libraries:rib-android-compose' diff --git a/android/tooling/rib-flipper-plugin/build.gradle b/android/tooling/rib-flipper-plugin/build.gradle index 543f014bd..687eee600 100644 --- a/android/tooling/rib-flipper-plugin/build.gradle +++ b/android/tooling/rib-flipper-plugin/build.gradle @@ -13,50 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" - -kotlin { - explicitApi() - jvmToolchain(11) +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.rib.flipper" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } } dependencies { - api project(":libraries:rib-android") - api project(":libraries:rib-android-core") - api project(":libraries:rib-base") - api deps.external.rxkotlin - api deps.external.rxrelay2 - api deps.external.rxjava2 - implementation deps.apt.javaxInject - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.external.guavaAndroid - implementation deps.external.flipper + api(project(":libraries:rib-android")) + api(project(":libraries:rib-android-core")) + api(project(":libraries:rib-base")) + api(libs.rxkotlin) + api(libs.rxrelay2) + api(libs.rxjava2) + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.guava.android) + implementation(libs.flipper) } diff --git a/android/tooling/rib-intellij-plugin/build.gradle b/android/tooling/rib-intellij-plugin/build.gradle index 4a553c7f1..9975a418d 100644 --- a/android/tooling/rib-intellij-plugin/build.gradle +++ b/android/tooling/rib-intellij-plugin/build.gradle @@ -1,20 +1,14 @@ -buildscript { - dependencies { - classpath deps.build.intellijPlugin - } -} - -apply plugin: "org.jetbrains.intellij" -apply plugin: "org.jetbrains.kotlin.jvm" - -kotlin { - jvmToolchain(11) +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.intellij) } group "com.uber.rib" repositories { mavenLocal() + google() + mavenCentral() } dependencies { @@ -35,16 +29,16 @@ dependencies { testImplementation(project(":libraries:rib-compiler-test")) { transitive = false } - testImplementation deps.apt.daggerCompiler - testImplementation deps.apt.javaxInject - testImplementation deps.external.dagger - testImplementation deps.uber.autodispose - testImplementation deps.uber.autodisposeLifecycle - testImplementation deps.test.truth - testImplementation deps.test.compileTesting - testImplementation deps.test.mockito - testImplementation deps.androidx.annotations - testImplementation deps.apt.androidApi + testImplementation(libs.dagger.compiler) + testImplementation(libs.javax.inject) + testImplementation(libs.dagger.library) + testImplementation(libs.autodispose.library) + testImplementation(libs.autodispose.lifecycle) + testImplementation(testLibs.truth) + testImplementation(testLibs.compileTesting) + testImplementation(testLibs.mockito) + testImplementation(libs.annotation) + testImplementation(libs.android.api) } // Determines if the machine has Maven credentials. @@ -62,7 +56,7 @@ version = pluginXml.version intellij { plugins = ['java', 'Kotlin', 'android'] - version = deps.versions.intellij + version = libs.versions.intellij pluginName = "uber-ribs" updateSinceUntilBuild = false sandboxDir = "${project.gradle.gradleHomeDir}/caches/intellij" @@ -79,13 +73,5 @@ afterEvaluate { archives sourcesJar archives project.tasks.getByName("buildPlugin") } - tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - jvmTarget = "11" - } - } - tasks.withType(JavaCompile).all { - sourceCompatibility = "11" - targetCompatibility = "11" - } + } diff --git a/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/build.gradle b/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/build.gradle index 467891d58..84034ec23 100644 --- a/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/build.gradle +++ b/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/build.gradle @@ -13,47 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -apply plugin: 'com.android.library' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "com.vanniktech.maven.publish" +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) +} android { namespace "com.uber.debug.broadcast.rib" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdkVersion deps.build.minSdkVersion - targetSdkVersion deps.build.targetSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } } dependencies { - api project(":libraries:rib-android") - api project(":libraries:rib-android-core") - api project(":libraries:rib-base") - api project(":tooling:utils:intellij-broadcast-core") - api deps.external.rxkotlin - api deps.external.rxrelay2 - api deps.external.rxjava2 - implementation deps.apt.javaxInject - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.external.guavaAndroid - implementation deps.external.flipper + api(project(":libraries:rib-android")) + api(project(":libraries:rib-android-core")) + api(project(":libraries:rib-base")) + api(project(":tooling:utils:intellij-broadcast-core")) + api(libs.rxkotlin) + api(libs.rxrelay2) + api(libs.rxjava2) + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.guava.android) + implementation(libs.flipper) } diff --git a/android/tooling/utils/intellij-broadcast-core/build.gradle b/android/tooling/utils/intellij-broadcast-core/build.gradle index 4bd49ceee..ddb95555d 100644 --- a/android/tooling/utils/intellij-broadcast-core/build.gradle +++ b/android/tooling/utils/intellij-broadcast-core/build.gradle @@ -14,49 +14,25 @@ * limitations under the License. */ -apply plugin: 'com.android.library' -apply plugin: "com.vanniktech.maven.publish" - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-library-conventions") + alias(libs.plugins.mavenPublish) } android { namespace "com.uber.debug.broadcast.core" - compileSdk deps.build.compileSdkVersion - - defaultConfig { - minSdk deps.build.minSdkVersion - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - - buildFeatures { - buildConfig false - } - - testOptions { - unitTests { - includeAndroidResources = true - } - } } dependencies { - api project(":libraries:rib-android") - api project(":libraries:rib-android-core") - api project(":libraries:rib-base") - api deps.external.rxkotlin - api deps.external.rxrelay2 - api deps.external.rxjava2 - implementation deps.apt.javaxInject - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.external.guavaAndroid - implementation deps.external.gson + api(project(":libraries:rib-android")) + api(project(":libraries:rib-android-core")) + api(project(":libraries:rib-base")) + api(libs.rxkotlin) + api(libs.rxrelay2) + api(libs.rxjava2) + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.guava.android) + implementation(libs.gson) } diff --git a/android/tutorials/tutorial1/build.gradle b/android/tutorials/tutorial1/build.gradle index 7be775dab..779515e6f 100644 --- a/android/tutorials/tutorial1/build.gradle +++ b/android/tutorials/tutorial1/build.gradle @@ -14,60 +14,25 @@ * limitations under the License. */ -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.nullaway - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") } -apply plugin: 'net.ltgt.nullaway' -apply plugin: 'net.ltgt.errorprone' - android { namespace "com.uber.rib.tutorial1" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial1" - versionCode 1 - versionName "1.0" - } - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion - } - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true } } dependencies { - annotationProcessor project(":libraries:rib-compiler-test") - annotationProcessor deps.apt.daggerCompiler - annotationProcessor deps.apt.nullAway - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") - - errorproneJavac deps.build.errorProneJavac - errorprone deps.build.nullAway - errorprone deps.build.errorProne - errorprone deps.build.guavaJre + kapt(project(":libraries:rib-compiler-test")) + kapt(libs.dagger.compiler) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) } diff --git a/android/tutorials/tutorial2/build.gradle b/android/tutorials/tutorial2/build.gradle index 7b5908ff0..8da1d92e0 100644 --- a/android/tutorials/tutorial2/build.gradle +++ b/android/tutorials/tutorial2/build.gradle @@ -14,66 +14,29 @@ * limitations under the License. */ -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.nullaway - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") } -apply plugin: 'net.ltgt.errorprone' -apply plugin: 'net.ltgt.nullaway' - android { namespace "com.uber.rib.tutorial1" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial2" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - annotationProcessor deps.apt.nullAway - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.androidx.percent - implementation deps.external.dagger - implementation deps.external.rxbinding - compileOnly deps.apt.androidApi - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") - - errorproneJavac deps.build.errorProneJavac - errorprone deps.build.nullAway - errorprone deps.build.errorProne - errorprone deps.build.guavaJre + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.percent) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + compileOnly(libs.android.api) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) } tasks.withType(JavaCompile) { diff --git a/android/tutorials/tutorial3-completed/build.gradle b/android/tutorials/tutorial3-completed/build.gradle index c7cb7a869..e6fcb60bb 100644 --- a/android/tutorials/tutorial3-completed/build.gradle +++ b/android/tutorials/tutorial3-completed/build.gradle @@ -13,54 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.tutorial1" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial3" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.androidx.percent - implementation deps.external.guavaAndroid - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.percent) + implementation(libs.guava.android) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) } diff --git a/android/tutorials/tutorial3/build.gradle b/android/tutorials/tutorial3/build.gradle index 3b2b91025..7c98c5d3a 100644 --- a/android/tutorials/tutorial3/build.gradle +++ b/android/tutorials/tutorial3/build.gradle @@ -13,53 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.tutorial1" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial3" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - implementation project(":libraries:rib-android") - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.androidx.percent - compileOnly deps.apt.javax - testImplementation project(":libraries:rib-test") + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.percent) + compileOnly(libs.jsr250) + testImplementation(project(":libraries:rib-test")) } diff --git a/android/tutorials/tutorial4/build.gradle b/android/tutorials/tutorial4/build.gradle index ca89895fd..e5e6f4b4b 100644 --- a/android/tutorials/tutorial4/build.gradle +++ b/android/tutorials/tutorial4/build.gradle @@ -13,58 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } -} - -apply plugin: 'com.android.application' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } android { namespace "com.uber.rib.tutorial4" - compileSdk deps.build.compileSdkVersion defaultConfig { - minSdk deps.build.minSdkVersion - targetSdk deps.build.targetSdkVersion applicationId "com.uber.tutorial3" - versionCode 1 - versionName "1.0" - } - - // No need for lint. This is just a tutorial. - lint { - abortOnError false - quiet true - } - - compileOptions { - sourceCompatibility deps.build.javaVersion - targetCompatibility deps.build.javaVersion } } dependencies { - annotationProcessor deps.apt.autoValue - annotationProcessor deps.apt.daggerCompiler - annotationProcessor project(":libraries:rib-compiler-test") - implementation project(":libraries:rib-android") - implementation project(":libraries:rib-workflow") - implementation deps.androidx.annotations - implementation deps.androidx.appcompat - implementation deps.external.dagger - implementation deps.external.rxbinding - implementation deps.androidx.percent - implementation deps.external.guavaAndroid - compileOnly deps.apt.javax - compileOnly deps.apt.autoValueAnnotations - testImplementation project(":libraries:rib-test") + kapt(libs.autovalue.library) + kapt(libs.dagger.compiler) + kapt(project(":libraries:rib-compiler-test")) + implementation(project(":libraries:rib-android")) + implementation(project(":libraries:rib-workflow")) + implementation(libs.annotation) + implementation(libs.appcompat) + implementation(libs.dagger.library) + implementation(libs.rxbinding) + implementation(libs.percent) + implementation(libs.guava.android) + compileOnly(libs.jsr250) + compileOnly(libs.autovalue.annotations) + testImplementation(project(":libraries:rib-test")) } From 2e02191150e413935affb7c34fa2354eae38d475 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Tue, 13 Jun 2023 13:16:39 -0700 Subject: [PATCH 08/52] deprecate old worker --- .../rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt | 7 +++++++ .../src/main/kotlin/com/uber/rib/core/WorkerBinder.kt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index e127805e3..b6f378c72 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -31,6 +31,13 @@ import kotlinx.coroutines.CoroutineDispatcher * RibDispatchers.Default) from [Worker] will take priority over the one passed via a * [WorkerBinder.bind] call */ +@Deprecated( + message = + """ + [com.uber.rib.core.Worker] is deprecated in favor of [com.uber.rib.core.RibCoroutineWorker] + """, + replaceWith = ReplaceWith("RibCoroutineWorker"), +) public interface Worker { /** 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 ad17417d6..92e61fdea 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 @@ -44,6 +44,13 @@ private val Worker.bindingCoroutineContext: CoroutineContext get() = this.coroutineContext ?: EmptyCoroutineContext /** Helper class to bind to an interactor's lifecycle to translate it to a [Worker] lifecycle. */ +@Deprecated( + message = + """ + [com.uber.rib.core.Worker] is deprecated in favor of [com.uber.rib.core.RibCoroutineWorker] + """, + replaceWith = ReplaceWith("For binding a RibCoroutineWorker use its .bind extension function"), +) public object WorkerBinder { private var workerBinderListenerWeakRef: WeakReference? = null From 8587e7974e895fdeab7816bb7da3d9a416809fc4 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 14 Jun 2023 10:05:49 -0700 Subject: [PATCH 09/52] Drafting monitoring option for RIBs --- .../kotlin/com/uber/rib/core/Interactor.kt | 43 +++++++++- .../uber/rib/core/RibMonitoringListener.kt | 83 +++++++++++++++++++ .../uber/rib/core/InteractorAndRouterTest.kt | 37 +++++++++ 3 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt 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 bb5d95e43..28b8e1a3b 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 @@ -19,12 +19,14 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException +import com.uber.rib.core.RibLogger.logRibEvent import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable import javax.inject.Inject import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty +import kotlin.system.measureTimeMillis import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -102,14 +104,47 @@ public abstract class Interactor

>() : InteractorType { public open fun dispatchAttach(savedInstanceState: Bundle?) { _lifecycleFlow.tryEmit(InteractorEvent.ACTIVE) - (getPresenter() as? Presenter)?.dispatchLoad() - didBecomeActive(savedInstanceState) + + val presenter = (getPresenter() as? Presenter) + presenter?.let { + val presenterDidUnloadDuration = measureTimeMillis { it.dispatchLoad() } + logRibEvent( + it.javaClass.simpleName, + RibMonitorType.PRESENTER_DID_LOAD, + presenterDidUnloadDuration, + ) + } + + val interactorDidBecomeActiveDuration = measureTimeMillis { + didBecomeActive(savedInstanceState) + } + logRibEvent( + this.javaClass.simpleName, + RibMonitorType.INTERACTOR_DID_BECOME_ACTIVE, + interactorDidBecomeActiveDuration, + ) } public open fun dispatchDetach(): P { - (getPresenter() as? Presenter)?.dispatchUnload() - willResignActive() + val presenter = (getPresenter() as? Presenter) + presenter?.let { + val presenterDidUnloadDuration = measureTimeMillis { it.dispatchLoad() } + logRibEvent( + it.javaClass.simpleName, + RibMonitorType.PRESENTER_WILL_UNLOAD, + presenterDidUnloadDuration, + ) + } + + val interactorWillResignActiveDuration = measureTimeMillis { willResignActive() } + logRibEvent( + this.javaClass.simpleName, + RibMonitorType.INTERACTOR_WILL_RESIGN_ACTIVE, + interactorWillResignActiveDuration, + ) + _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) + return getPresenter() } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt new file mode 100644 index 000000000..935a97eef --- /dev/null +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt @@ -0,0 +1,83 @@ +/* + * 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 + +/** + * Reports duration of critical RIB events like Interactor.didBecomeActive/willResignActive or + * Presenter.didLoad/willUnload + */ +public object RibLogger { + + private var ribMonitoringListener: RibMonitoringListener? = null + + /** + * Initialize your monitoring at the earliest entry point of the app. + * + * This should only be set once + */ + @JvmStatic + public fun initialize(ribMonitoringListener: RibMonitoringListener) { + this.ribMonitoringListener = ribMonitoringListener + } + + /** + * Will report Rib event duration information only when [RibMonitoringListener] is specified (via + * RibLogger.initializeRibMonitor) + */ + @JvmStatic + public fun logRibEvent( + className: String, + eventType: RibMonitorType, + totalBindingDurationMilli: Long, + ) { + ribMonitoringListener?.let { + val ribMonitorData = + RibMonitorData( + className, + eventType, + Thread.currentThread().name, + totalBindingDurationMilli, + ) + it.onRibEventCompleted(ribMonitorData) + } + } +} + +public enum class RibMonitorType { + INTERACTOR_DID_BECOME_ACTIVE, + INTERACTOR_WILL_RESIGN_ACTIVE, + PRESENTER_DID_LOAD, + PRESENTER_WILL_UNLOAD, +} + +public data class RibMonitorData( + /** Related RIB class name */ + val className: String, + + /** E.g. Interactor.didBecomeActive, Presenter.willLoad */ + val ribEventMonitorType: RibMonitorType, + + /** Reports the current thread name where Rib Event happen (should mainly be main thread) */ + val threadName: String, + + /** Total binding duration in milliseconds of Worker.onStart/onStop */ + val totalBindingDurationMilli: Long, +) + +/** Reports duration of related RibEvents (e.g. Interactor.didBecomeActive/Presenter.diLoad) */ +public interface RibMonitoringListener { + public fun onRibEventCompleted(ribMonitorData: RibMonitorData) +} diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 4faa01c6a..6e489fab0 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -22,6 +22,8 @@ import com.uber.rib.core.lifecycle.InteractorEvent import org.junit.Before import org.junit.Test import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -33,6 +35,7 @@ class InteractorAndRouterTest { private val childInteractor: Interactor<*, *> = mock() private val ribRefWatcher: RibRefWatcher = mock() + private val ribMonitoringListener: RibMonitoringListener = mock() private lateinit var interactor: TestInteractor private lateinit var router: TestRouter @@ -68,6 +71,40 @@ class InteractorAndRouterTest { verify(childInteractor).dispatchDetach() } + @Test + fun dispatchAttach_withMonitoringEnabled_shouldReportDuration() { + // Given. + val ribMonitorDataCaptor = argumentCaptor() + RibLogger.initialize(ribMonitoringListener) + + // When. + router.dispatchAttach(null) + + // Then. + verify(ribMonitoringListener, atLeastOnce()).onRibEventCompleted(ribMonitorDataCaptor.capture()) + Truth.assertThat(ribMonitorDataCaptor.firstValue.ribEventMonitorType) + .isEqualTo(RibMonitorType.PRESENTER_DID_LOAD) + Truth.assertThat(ribMonitorDataCaptor.secondValue.ribEventMonitorType) + .isEqualTo(RibMonitorType.INTERACTOR_DID_BECOME_ACTIVE) + } + + @Test + fun dispatchDetach_withMonitoringEnabled_shouldReportDuration() { + // Given. + val ribMonitorDataCaptor = argumentCaptor() + RibLogger.initialize(ribMonitoringListener) + + // When. + router.dispatchDetach() + + // Then. + verify(ribMonitoringListener, atLeastOnce()).onRibEventCompleted(ribMonitorDataCaptor.capture()) + Truth.assertThat(ribMonitorDataCaptor.firstValue.ribEventMonitorType) + .isEqualTo(RibMonitorType.PRESENTER_WILL_UNLOAD) + Truth.assertThat(ribMonitorDataCaptor.secondValue.ribEventMonitorType) + .isEqualTo(RibMonitorType.INTERACTOR_WILL_RESIGN_ACTIVE) + } + @Test @Throws(Exception::class) fun correspondingEvents_whenActive_shouldReturnInactive() { From 1930713be2b37c11b400b8f846891a066323d3ed Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Tue, 13 Jun 2023 11:36:20 -0700 Subject: [PATCH 10/52] Update worker binder dispatcher --- .../RibWorkerSelectionInteractor.kt | 5 +- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 53 +++++++++++-------- .../com/uber/rib/core/WorkerBinderTest.kt | 15 ++++++ 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt index d7d39f30d..02b28cc4e 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt @@ -18,7 +18,6 @@ package com.uber.rib.workers.root.main.ribworkerselection import com.uber.rib.core.BasicInteractor import com.uber.rib.core.Bundle import com.uber.rib.core.ComposePresenter -import com.uber.rib.core.RibDispatchers import com.uber.rib.core.WorkerBinder import com.uber.rib.core.asRibCoroutineWorker import com.uber.rib.core.asWorker @@ -62,9 +61,7 @@ class RibWorkerSelectionInteractor( RibWorkerBindTypeClickType.BIND_MULTIPLE_WORKERS -> { val workers = listOf(backgroundWorker, defaultWorker, ioWorker, uiWorker) updateViewModel("Multiple workers ") - // Given uiWorker specifies its CoroutineContext, - // RibDispatchers.Main for Ui worker will remain besides Dispatchers.Default - WorkerBinder.bind(this, workers, RibDispatchers.Default) + WorkerBinder.bind(this, workers) } RibWorkerBindTypeClickType.BIND_RIB_COROUTINE_WORKER -> { updateViewModel(defaultRibCoroutineWorker::class.simpleName) 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 92e61fdea..a42e21049 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 @@ -55,6 +55,10 @@ public object WorkerBinder { private var workerBinderListenerWeakRef: WeakReference? = null + private var workerBinderDispatcher: WorkerBinderDispatcher = WorkerBinderDispatcher { + RibDispatchers.Unconfined + } + /** * Initializes reporting of [WorkerBinderInfo] via [WorkerBinderListener] * @@ -66,6 +70,19 @@ public object WorkerBinder { this.workerBinderListenerWeakRef = WeakReference(workerBinderListener) } + /** + * WARNING: Use with caution + * + * Changes the default WorkerBinderDispatcher used for all WorkerBinder calls. When not updated + * will rely on [RibDispatcher.Unconfined] for previous backward compatibility. + * + * To be called at the earliest point of your application + */ + @JvmStatic + public fun updateDispatcher(workerBinderDispatcher: WorkerBinderDispatcher) { + this.workerBinderDispatcher = workerBinderDispatcher + } + /** * Bind a worker (ie. a manager or any other class that needs an interactor's lifecycle) to an * interactor's lifecycle events. Inject this class into your interactor and call this method on @@ -73,21 +90,17 @@ public object WorkerBinder { * * @param interactor The interactor that provides the lifecycle. * @param worker The class that wants to be informed when to start and stop doing work. - * @param dispatcherAtBinder CoroutineDispatcher to be apply only when [Worker.coroutineContext] - * is not overriden with a value different that [EmptyCoroutineContext] * @return [WorkerUnbinder] to unbind [Worker]'s lifecycle. */ @JvmStatic - @JvmOverloads public fun bind( interactor: Interactor<*, *>, worker: Worker, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, ): WorkerUnbinder = worker.bind( interactor.lifecycleFlow, Interactor.lifecycleRange, - dispatcherAtBinder, + workerBinderDispatcher.get(), workerBinderListenerWeakRef, ) @@ -98,19 +111,14 @@ public object WorkerBinder { * * @param interactor The interactor that provides the lifecycle. * @param workers A list of classes that want to be informed when to start and stop doing work. - * @param dispatcherAtBinder CoroutineDispatcher to be applied only when the - * [Worker.coroutineContext] is not overriden with a value different than - * [EmptyCoroutineContext] */ @JvmStatic - @JvmOverloads public fun bind( interactor: Interactor<*, *>, workers: List, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, ) { for (interactorWorker in workers) { - bind(interactor, interactorWorker, dispatcherAtBinder) + bind(interactor, interactorWorker) } } @@ -120,22 +128,17 @@ public object WorkerBinder { * * @param presenter The presenter that provides the lifecycle. * @param worker The class that wants to be informed when to start and stop doing work. - * @param dispatcherAtBinder CoroutineDispatcher to be applied only when the - * [Worker.coroutineContext] is not overriden with a value different than - * [EmptyCoroutineContext] * @return [WorkerUnbinder] to unbind [Worker]'s lifecycle. */ @JvmStatic - @JvmOverloads public fun bind( presenter: Presenter, worker: Worker, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, ): WorkerUnbinder = worker.bind( presenter.lifecycleFlow, Presenter.lifecycleRange, - dispatcherAtBinder, + workerBinderDispatcher.get(), workerBinderListenerWeakRef, ) @@ -146,18 +149,14 @@ public object WorkerBinder { * * @param presenter The presenter that provides the lifecycle. * @param workers A list of classes that want to be informed when to start and stop doing work. - * @param dispatcherAtBinder CoroutineDispatcher to be applied only when the - * [Worker.coroutineContext] is not overriden with a value different than - * [EmptyCoroutineContext] */ @JvmStatic public fun bind( presenter: Presenter, workers: List, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, ) { for (worker in workers) { - bind(presenter, worker, dispatcherAtBinder) + bind(presenter, worker) } } @@ -278,6 +277,14 @@ public data class WorkerBinderInfo( val totalBindingDurationMilli: Long, ) +/* + * The CoroutineDispatcher to be applied only when [Worker.coroutineContext] + * is not overriden with a value different that [EmptyCoroutineContext] + */ +public fun interface WorkerBinderDispatcher { + public fun get(): CoroutineDispatcher +} + /** Reports total binding duration of Worker.onStart/onStop */ public fun interface WorkerBinderListener { @@ -305,7 +312,7 @@ private fun getJobCoroutineContext( private fun > Worker.bind( lifecycle: SharedFlow, lifecycleRange: ClosedRange, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, + dispatcherAtBinder: CoroutineDispatcher, workerDurationListenerWeakRef: WeakReference?, ): WorkerUnbinder { val coroutineContext = diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 63cc9c7d9..1a4a9e2c5 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -62,6 +62,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { WorkerBinder.initializeMonitoring(workerBinderListener) + WorkerBinder.updateDispatcher { RibDispatchers.Unconfined } } @Test @@ -223,6 +224,20 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { ) } + @Test + fun bind_withUpdatedWorkerBinderDispatcher_shouldBindOnRibDispatchersDefault() = runTest { + val binderDurationCaptor = argumentCaptor() + WorkerBinder.updateDispatcher { RibDispatchers.Default } + prepareInteractor() + bind(interactor, fakeWorker) + advanceUntilIdle() + verify(workerBinderListener).onBindCompleted(binderDurationCaptor.capture()) + binderDurationCaptor.firstValue.assertWorkerDuration( + "FakeWorker", + WorkerEvent.START, + RibDispatchers.Default, + ) + } private fun bindFakeWorker(): WorkerUnbinder { prepareInteractor() return bind(interactor, fakeWorker) From 880bbb6d97849ef5470e7422ce360279ce59e446 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 14 Jun 2023 16:15:08 -0700 Subject: [PATCH 11/52] Include deprecated WorkerBinder dispatcher - Define a dispatchers to be used on the now deprecated WorkerBinder/Worker within RibCoroutineConfig --- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 29 +------------------ .../com/uber/rib/core/WorkerBinderTest.kt | 15 ---------- .../com/uber/rib/core/RibCoroutinesConfig.kt | 17 +++++++++++ 3 files changed, 18 insertions(+), 43 deletions(-) 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 a42e21049..102ce8d6f 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 @@ -55,10 +55,6 @@ public object WorkerBinder { private var workerBinderListenerWeakRef: WeakReference? = null - private var workerBinderDispatcher: WorkerBinderDispatcher = WorkerBinderDispatcher { - RibDispatchers.Unconfined - } - /** * Initializes reporting of [WorkerBinderInfo] via [WorkerBinderListener] * @@ -70,19 +66,6 @@ public object WorkerBinder { this.workerBinderListenerWeakRef = WeakReference(workerBinderListener) } - /** - * WARNING: Use with caution - * - * Changes the default WorkerBinderDispatcher used for all WorkerBinder calls. When not updated - * will rely on [RibDispatcher.Unconfined] for previous backward compatibility. - * - * To be called at the earliest point of your application - */ - @JvmStatic - public fun updateDispatcher(workerBinderDispatcher: WorkerBinderDispatcher) { - this.workerBinderDispatcher = workerBinderDispatcher - } - /** * Bind a worker (ie. a manager or any other class that needs an interactor's lifecycle) to an * interactor's lifecycle events. Inject this class into your interactor and call this method on @@ -100,7 +83,6 @@ public object WorkerBinder { worker.bind( interactor.lifecycleFlow, Interactor.lifecycleRange, - workerBinderDispatcher.get(), workerBinderListenerWeakRef, ) @@ -138,7 +120,6 @@ public object WorkerBinder { worker.bind( presenter.lifecycleFlow, Presenter.lifecycleRange, - workerBinderDispatcher.get(), workerBinderListenerWeakRef, ) @@ -277,14 +258,6 @@ public data class WorkerBinderInfo( val totalBindingDurationMilli: Long, ) -/* - * The CoroutineDispatcher to be applied only when [Worker.coroutineContext] - * is not overriden with a value different that [EmptyCoroutineContext] - */ -public fun interface WorkerBinderDispatcher { - public fun get(): CoroutineDispatcher -} - /** Reports total binding duration of Worker.onStart/onStop */ public fun interface WorkerBinderListener { @@ -312,9 +285,9 @@ private fun getJobCoroutineContext( private fun > Worker.bind( lifecycle: SharedFlow, lifecycleRange: ClosedRange, - dispatcherAtBinder: CoroutineDispatcher, workerDurationListenerWeakRef: WeakReference?, ): WorkerUnbinder { + val dispatcherAtBinder = RibCoroutinesConfig.deprecatedWorkerDispatcher val coroutineContext = getJobCoroutineContext( dispatcherAtBinder, diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 1a4a9e2c5..63cc9c7d9 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -62,7 +62,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { WorkerBinder.initializeMonitoring(workerBinderListener) - WorkerBinder.updateDispatcher { RibDispatchers.Unconfined } } @Test @@ -224,20 +223,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { ) } - @Test - fun bind_withUpdatedWorkerBinderDispatcher_shouldBindOnRibDispatchersDefault() = runTest { - val binderDurationCaptor = argumentCaptor() - WorkerBinder.updateDispatcher { RibDispatchers.Default } - prepareInteractor() - bind(interactor, fakeWorker) - advanceUntilIdle() - verify(workerBinderListener).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.firstValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.START, - RibDispatchers.Default, - ) - } private fun bindFakeWorker(): WorkerUnbinder { prepareInteractor() return bind(interactor, fakeWorker) diff --git a/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutinesConfig.kt b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutinesConfig.kt index e083ce4e3..887bcf4ca 100644 --- a/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutinesConfig.kt +++ b/android/libraries/rib-coroutines/src/main/kotlin/com/uber/rib/core/RibCoroutinesConfig.kt @@ -37,4 +37,21 @@ public object RibCoroutinesConfig { * [Thread.UncaughtExceptionHandler]. */ @JvmStatic public var exceptionHandler: CoroutineExceptionHandler? = null + + /** + * Specify the [CoroutineDispatcher] to be used while binding a [com.uber.rib.Worker] via + * [WorkerBinder] + * + * IMPORTANT: This shouldn't be used outside [WorkerBinder] given that [WorkerBinder]/[Worker] are + * deprecated + */ + @Deprecated( + message = + """ + This dispatcher is only intended to be used within the [com.uber.rib.core.WorkerBinder]. + For adding and binding new RIB workers please use [RibCoroutineWorker] + """, + ) + @JvmStatic + public var deprecatedWorkerDispatcher: CoroutineDispatcher = RibDispatchers.Unconfined } From 789d7bf8fb9f81d733fe299c87b7d812033aed7f Mon Sep 17 00:00:00 2001 From: Fran Aguilera Date: Wed, 14 Jun 2023 22:19:45 -0700 Subject: [PATCH 12/52] Update README.md https://github.com/uber/RIBs/issues/598 Fixing previous broken link https://eng.uber.com/new-rider-app with https://www.uber.com/blog/new-rider-app-architecture/ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81f5be082..23b4a8d28 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To get started with RIBs, please refer to the [RIBs documentation](https://githu To get more hands on with RIBs, we have written a [series of tutorials](https://github.com/uber/RIBs/wiki) that run you through the main aspects of the architecture with hands-on examples. -To read about the backstory on why we created RIBs, see [this blog post](https://eng.uber.com/new-rider-app/) we wrote when releasing RIBs in production the first time and see [this short video](https://www.youtube.com/watch?v=Q5cTT0M0YXg) where we discussed how the RIBs architecture works. +To read about the backstory on why we created RIBs, see [this blog post](https://www.uber.com/blog/new-rider-app-architecture/) we wrote when releasing RIBs in production the first time and see [this short video](https://www.youtube.com/watch?v=Q5cTT0M0YXg) where we discussed how the RIBs architecture works. #### What is the difference between RIBs and MV*/VIPER? From 66eac33013eeca307f353afd28e2acae2d6aeffb Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 15 Jun 2023 15:03:19 -0700 Subject: [PATCH 13/52] Expose stream for RIB component duration info --- android/demos/intellij/README.md | 2 +- .../java/com/uber/rib/SampleApplication.java | 2 +- .../uber/rib/workers/ComposeApplication.kt | 9 ++ .../uber/rib/workers/root/RootInteractor.kt | 14 +-- .../com/uber/rib/workers/root/RootScope.kt | 8 -- .../rib/workers/root/logger/RibEventLogger.kt | 43 ++++++++ .../workers/monitoring/RibWorkerMonitor.kt | 63 ------------ .../kotlin/com/uber/rib/core/RibActivity.kt | 4 +- .../kotlin/com/uber/rib/core/Interactor.kt | 36 ++++--- .../com/uber/rib/core/RibComponentType.kt} | 11 ++- .../kotlin/com/uber/rib/core/RibEvents.kt | 70 +++++++++++-- .../uber/rib/core/RibMonitoringListener.kt | 83 ---------------- .../core/{RibEvent.kt => RibRouterEvent.kt} | 2 +- .../main/kotlin/com/uber/rib/core/Router.kt | 21 +++- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 98 +++---------------- .../uber/rib/core/InteractorAndRouterTest.kt | 37 ------- .../com/uber/rib/core/WorkerBinderTest.kt | 50 ++-------- .../com/uber/rib/flipper/RibTreePlugin.kt | 11 +-- .../RibHierarchyDebugBroadcastHandler.java | 20 ++-- 19 files changed, 201 insertions(+), 383 deletions(-) create mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt delete mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/RibWorkerMonitor.kt rename android/{demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt => libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt} (76%) delete mode 100644 android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt rename android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/{RibEvent.kt => RibRouterEvent.kt} (96%) diff --git a/android/demos/intellij/README.md b/android/demos/intellij/README.md index 60b6f717c..b0b31533c 100644 --- a/android/demos/intellij/README.md +++ b/android/demos/intellij/README.md @@ -22,7 +22,7 @@ public class SampleApplication extends Application { this, Arrays.asList( new RibHierarchyDebugBroadcastHandler( - getApplicationContext(), RibEvents.getInstance().getEvents()))); + getApplicationContext(), RibEvents.getRouterEvents()))); } } } diff --git a/android/demos/intellij/src/main/java/com/uber/rib/SampleApplication.java b/android/demos/intellij/src/main/java/com/uber/rib/SampleApplication.java index d7d788067..3be361139 100644 --- a/android/demos/intellij/src/main/java/com/uber/rib/SampleApplication.java +++ b/android/demos/intellij/src/main/java/com/uber/rib/SampleApplication.java @@ -34,7 +34,7 @@ public void onCreate() { this, Arrays.asList( new RibHierarchyDebugBroadcastHandler( - getApplicationContext(), RibEvents.getInstance().getEvents()))); + getApplicationContext(), RibEvents.getRouterEvents()))); } } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt index cc9d2adcc..1696afdaf 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt @@ -22,10 +22,17 @@ import com.facebook.flipper.core.FlipperClient import com.facebook.flipper.plugins.inspector.DescriptorMapping import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin import com.facebook.soloader.SoLoader +import com.uber.rib.core.RibEvents import com.uber.rib.flipper.RibTreePlugin +import com.uber.rib.workers.root.logger.RibEventLogger +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.rx2.asFlow class ComposeApplication : Application() { + @OptIn(DelicateCoroutinesApi::class) override fun onCreate() { super.onCreate() SoLoader.init(this, false) @@ -36,5 +43,7 @@ class ComposeApplication : Application() { client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults())) client.start() } + + GlobalScope.launch { RibEvents.ribDurationEvents.asFlow().collect { RibEventLogger.log(it) } } } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootInteractor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootInteractor.kt index b17d68d9a..a5d310f58 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootInteractor.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootInteractor.kt @@ -16,20 +16,8 @@ package com.uber.rib.workers.root import com.uber.rib.core.BasicInteractor -import com.uber.rib.core.Bundle import com.uber.rib.core.EmptyPresenter -import com.uber.rib.core.WorkerBinder -import com.uber.rib.workers.root.main.workers.monitoring.RibWorkerMonitor class RootInteractor( presenter: EmptyPresenter, - private val ribWorkerMonitor: RibWorkerMonitor, -) : BasicInteractor(presenter) { - - override fun didBecomeActive(savedInstanceState: Bundle?) { - super.didBecomeActive(savedInstanceState) - - // Setting up Worker Monitoring prior to any WorkerBinding - WorkerBinder.initializeMonitoring(ribWorkerMonitor) - } -} +) : BasicInteractor(presenter) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt index 8c354c2b5..e5e025913 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt @@ -21,8 +21,6 @@ import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.uber.rib.core.EmptyPresenter import com.uber.rib.core.RibActivity import com.uber.rib.workers.root.main.MainScope -import com.uber.rib.workers.root.main.workers.monitoring.BackendReporter -import com.uber.rib.workers.root.main.workers.monitoring.RibWorkerMonitor @motif.Scope interface RootScope { @@ -44,11 +42,5 @@ interface RootScope { setViewTreeSavedStateRegistryOwner(activity) } } - - fun backendReporter(): BackendReporter = BackendReporter {} - - fun ribWorkerMonitor(backendReporter: BackendReporter): RibWorkerMonitor { - return RibWorkerMonitor(backendReporter) - } } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt new file mode 100644 index 000000000..af897b9db --- /dev/null +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt @@ -0,0 +1,43 @@ +/* + * 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.workers.root.logger + +import android.util.Log +import com.uber.rib.core.RibEventDurationData +import com.uber.rib.workers.root.logger.RibEventLogger.log + +/** + * Sample of consuming [ribEventDataFlow] possibilities. + * 1. Can pipe Interactor/Router/Presenter/Worker information to backend + * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection + * 3. More tailored aggregation if needed. + * + * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker + * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance + */ +object RibEventLogger { + + private const val LOG_TAG = "RibEventLogger" + + fun log(ribEventData: RibEventDurationData) { + val message = ribEventData.buildWorkerDurationMessage() + Log.d(LOG_TAG, message) + } + + private fun RibEventDurationData.buildWorkerDurationMessage(): String { + return "RibEventDurationData: ${this.ribComponentType}_${this.ribEventType} at ${this.className} took ${this.totalBindingDurationMilli} ms in thread: ${this.threadName}" + } +} diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/RibWorkerMonitor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/RibWorkerMonitor.kt deleted file mode 100644 index 35ffec8ea..000000000 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/RibWorkerMonitor.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.workers.root.main.workers.monitoring - -import android.util.Log -import com.uber.rib.core.WorkerBinderInfo -import com.uber.rib.core.WorkerBinderListener -import com.uber.rib.workers.BuildConfig -import java.lang.Exception - -/** - * Sample of a custom [WorkerBinderListener] possibilities. - * 1. Could pipe Worker Binder information to backend - * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection - * 3. More tailored aggregation if needed. - * - * IMPORTANT: Given logic of [onBindCompleted] will be running upon each Worker onStart/onStop. - * Added logic should guaranteed that we are not impacting app performance by having expensive logic - * within its implementation. - */ -class RibWorkerMonitor(private val backendReporter: BackendReporter) : WorkerBinderListener { - - override fun onBindCompleted(workerBinderInfo: WorkerBinderInfo) { - val message = workerBinderInfo.buildWorkerDurationMessage() - Log.d(LOG_TAG, message) - - backendReporter.report(message) - - if (BuildConfig.DEBUG && workerBinderInfo.isExpensiveUiWorker()) { - throw ExpensiveUiWorkerException(message) - } - } - - private fun WorkerBinderInfo.isExpensiveUiWorker(): Boolean { - return this.threadName.contains(MAIN_THREAD_IDENTIFIER) && - this.totalBindingDurationMilli > MAIN_THRESHOLD_MILLI - } - - private fun WorkerBinderInfo.buildWorkerDurationMessage(): String { - return "WorkerBinderInfo: ${this.workerName} ${this.workerEvent} took ${this.totalBindingDurationMilli} ms. [${this.coroutineContext}] - [Thread: ${this.threadName}] [Total threads: ${Thread.activeCount()}]" - } - - companion object { - private const val LOG_TAG = "RibWorkerMonitor" - private const val MAIN_THREAD_IDENTIFIER = "main" - private const val MAIN_THRESHOLD_MILLI = 16 - } -} - -class ExpensiveUiWorkerException(message: String) : Exception(message) 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 493144c04..04400d736 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 @@ -98,7 +98,7 @@ abstract class RibActivity : router?.let { it.dispatchAttach(wrappedBundle) rootViewGroup.addView(it.view) - RibEvents.getInstance().emitEvent(RibEventType.ATTACHED, it, null) + RibEvents.emitRouterEvent(RibEventType.ATTACHED, it, null) } } @@ -151,7 +151,7 @@ abstract class RibActivity : _lifecycleFlow.tryEmit(create(ActivityLifecycleEvent.Type.DESTROY)) router?.let { it.dispatchDetach() - RibEvents.getInstance().emitEvent(RibEventType.DETACHED, it, null) + RibEvents.emitRouterEvent(RibEventType.DETACHED, it, null) } router = null super.onDestroy() 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 28b8e1a3b..9a75d55e0 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 @@ -19,7 +19,7 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException -import com.uber.rib.core.RibLogger.logRibEvent +import com.uber.rib.core.RibEvents.emitRibEventDuration import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -107,20 +107,22 @@ public abstract class Interactor

>() : InteractorType { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidUnloadDuration = measureTimeMillis { it.dispatchLoad() } - logRibEvent( - it.javaClass.simpleName, - RibMonitorType.PRESENTER_DID_LOAD, - presenterDidUnloadDuration, + val presenterDidLoadDuration = measureTimeMillis { it.dispatchLoad() } + emitRibEventDuration( + it.javaClass.kotlin, + RibComponentType.PRESENTER, + RibEventType.ATTACHED, + presenterDidLoadDuration, ) } val interactorDidBecomeActiveDuration = measureTimeMillis { didBecomeActive(savedInstanceState) } - logRibEvent( - this.javaClass.simpleName, - RibMonitorType.INTERACTOR_DID_BECOME_ACTIVE, + emitRibEventDuration( + this.javaClass.kotlin, + RibComponentType.INTERACTOR, + RibEventType.ATTACHED, interactorDidBecomeActiveDuration, ) } @@ -128,18 +130,20 @@ public abstract class Interactor

>() : InteractorType { public open fun dispatchDetach(): P { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidUnloadDuration = measureTimeMillis { it.dispatchLoad() } - logRibEvent( - it.javaClass.simpleName, - RibMonitorType.PRESENTER_WILL_UNLOAD, + val presenterDidUnloadDuration = measureTimeMillis { it.dispatchUnload() } + emitRibEventDuration( + it.javaClass.kotlin, + RibComponentType.PRESENTER, + RibEventType.DETACHED, presenterDidUnloadDuration, ) } val interactorWillResignActiveDuration = measureTimeMillis { willResignActive() } - logRibEvent( - this.javaClass.simpleName, - RibMonitorType.INTERACTOR_WILL_RESIGN_ACTIVE, + emitRibEventDuration( + this.javaClass.kotlin, + RibComponentType.INTERACTOR, + RibEventType.DETACHED, interactorWillResignActiveDuration, ) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt similarity index 76% rename from android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt rename to android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt index 5a838c90b..8f9c6308b 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt @@ -13,8 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.uber.rib.workers.root.main.workers.monitoring +package com.uber.rib.core -fun interface BackendReporter { - fun report(genericMessage: String) +public enum class RibComponentType { + ROUTER, + PRESENTER, + INTERACTOR, + DEPRECATED_WORKER, + + /** RIB_COROUTINE_WORKER -> To be added on next releases */ } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 772b44d84..446c1751a 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -16,14 +16,25 @@ package com.uber.rib.core import io.reactivex.Observable +import kotlin.reflect.KClass import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.rx2.asObservable -public class RibEvents private constructor() { +public object RibEvents { - private val _events = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) - public val events: Observable = _events.asObservable() + private val mutableRouterEvents = + MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) + private val mutableRibDurationEvents = + MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) + + @JvmStatic + public val routerEvents: Observable + get() = mutableRouterEvents.asObservable() + + @JvmStatic + public val ribDurationEvents: Observable + get() = mutableRibDurationEvents.asObservable() /** * @param eventType [RibEventType] @@ -31,13 +42,56 @@ public class RibEvents private constructor() { * @param parent [Router] and null for the root ribs that are directly attached to * RibActivity/Fragment */ - public fun emitEvent(eventType: RibEventType, child: Router<*>, parent: Router<*>?) { - _events.tryEmit(RibEvent(eventType, child, parent)) + public fun emitRouterEvent(eventType: RibEventType, child: Router<*>, parent: Router<*>?) { + mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent)) } - public companion object { - private val instance: RibEvents = RibEvents() + /** + * Emits emission of ATTACHED/DETACHED events for each RIB component. + * + * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, + * UiRibWorker, etc) + * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) + * @param ribEventType RIB event type (e.g. ATTACH/DETACH) + * @param totalBindingDurationMilli Total duration (in ms) of each ATTACH/DETACH events + */ + internal fun emitRibEventDuration( + ribClass: KClass<*>, + ribComponentType: RibComponentType, + ribEventType: RibEventType, + totalBindingDurationMilli: Long, + ) { + val ribClassName = ribClass.qualifiedName - @JvmStatic public fun getInstance(): RibEvents = instance + // There's no point to emit emission if we don't know which RIB component name was triggered + ribClassName?.let { + val ribEventData = + RibEventDurationData( + it, + ribComponentType, + ribEventType, + Thread.currentThread().name, + totalBindingDurationMilli, + ) + mutableRibDurationEvents.tryEmit(ribEventData) + } } } + +/** Holds relevant RIB event information */ +public data class RibEventDurationData( + /** Related RIB class name */ + val className: String, + + /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ + val ribComponentType: RibComponentType, + + /** RIB component event type ATTACHED/DETACHED */ + val ribEventType: RibEventType, + + /** Reports the current thread name where Rib Event happen (should mainly be main thread) */ + val threadName: String, + + /** Total binding duration in milliseconds of Worker.onStart/onStop */ + val totalBindingDurationMilli: Long, +) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt deleted file mode 100644 index 935a97eef..000000000 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibMonitoringListener.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 - -/** - * Reports duration of critical RIB events like Interactor.didBecomeActive/willResignActive or - * Presenter.didLoad/willUnload - */ -public object RibLogger { - - private var ribMonitoringListener: RibMonitoringListener? = null - - /** - * Initialize your monitoring at the earliest entry point of the app. - * - * This should only be set once - */ - @JvmStatic - public fun initialize(ribMonitoringListener: RibMonitoringListener) { - this.ribMonitoringListener = ribMonitoringListener - } - - /** - * Will report Rib event duration information only when [RibMonitoringListener] is specified (via - * RibLogger.initializeRibMonitor) - */ - @JvmStatic - public fun logRibEvent( - className: String, - eventType: RibMonitorType, - totalBindingDurationMilli: Long, - ) { - ribMonitoringListener?.let { - val ribMonitorData = - RibMonitorData( - className, - eventType, - Thread.currentThread().name, - totalBindingDurationMilli, - ) - it.onRibEventCompleted(ribMonitorData) - } - } -} - -public enum class RibMonitorType { - INTERACTOR_DID_BECOME_ACTIVE, - INTERACTOR_WILL_RESIGN_ACTIVE, - PRESENTER_DID_LOAD, - PRESENTER_WILL_UNLOAD, -} - -public data class RibMonitorData( - /** Related RIB class name */ - val className: String, - - /** E.g. Interactor.didBecomeActive, Presenter.willLoad */ - val ribEventMonitorType: RibMonitorType, - - /** Reports the current thread name where Rib Event happen (should mainly be main thread) */ - val threadName: String, - - /** Total binding duration in milliseconds of Worker.onStart/onStop */ - val totalBindingDurationMilli: Long, -) - -/** Reports duration of related RibEvents (e.g. Interactor.didBecomeActive/Presenter.diLoad) */ -public interface RibMonitoringListener { - public fun onRibEventCompleted(ribMonitorData: RibMonitorData) -} diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvent.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRouterEvent.kt similarity index 96% rename from android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvent.kt rename to android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRouterEvent.kt index bc31dfaca..74f560e64 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvent.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRouterEvent.kt @@ -21,7 +21,7 @@ package com.uber.rib.core * @param parentRouter [Router] and null for the root ribs that are directly attached to * RibActivity/Fragment */ -public open class RibEvent( +public open class RibRouterEvent( public open val eventType: RibEventType, public open val router: Router<*>, public open val parentRouter: Router<*>?, 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 37041ffc8..70760477b 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 @@ -21,6 +21,7 @@ import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList +import kotlin.system.measureTimeMillis /** * Responsible for handling the addition and removal of children routers. @@ -113,13 +114,19 @@ protected constructor( ) } } - children.add(childRouter) + val attachedRouterDurationInMilli = measureTimeMillis { children.add(childRouter) } + RibEvents.emitRibEventDuration( + this.javaClass.kotlin, + RibComponentType.ROUTER, + RibEventType.ATTACHED, + attachedRouterDurationInMilli, + ) ribRefWatcher.logBreadcrumb( "ATTACHED", childRouter.javaClass.simpleName, this.javaClass.simpleName, ) - RibEvents.getInstance().emitEvent(RibEventType.ATTACHED, childRouter, this) + RibEvents.emitRouterEvent(RibEventType.ATTACHED, childRouter, this) var childBundle: Bundle? = null if (savedInstanceState != null) { val previousChildren = savedInstanceState?.getBundleExtra(KEY_CHILD_ROUTERS) @@ -157,9 +164,15 @@ protected constructor( .handleNonFatalWarning("A RIB tried to detach a child that was never attached", null) } } - childRouter.dispatchDetach() + val routerDetachDurationInMilli = measureTimeMillis { childRouter.dispatchDetach() } if (isChildRemoved) { - RibEvents.getInstance().emitEvent(RibEventType.DETACHED, childRouter, this) + RibEvents.emitRibEventDuration( + this.javaClass.kotlin, + RibComponentType.ROUTER, + RibEventType.DETACHED, + routerDetachDurationInMilli, + ) + RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) } } 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 ad17417d6..5a5e139b0 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 @@ -18,12 +18,12 @@ package com.uber.rib.core import androidx.annotation.VisibleForTesting import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider +import com.uber.rib.core.RibEvents.emitRibEventDuration import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent import io.reactivex.Observable import io.reactivex.subjects.CompletableSubject -import java.lang.ref.WeakReference import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.system.measureTimeMillis @@ -46,19 +46,6 @@ private val Worker.bindingCoroutineContext: CoroutineContext /** Helper class to bind to an interactor's lifecycle to translate it to a [Worker] lifecycle. */ public object WorkerBinder { - private var workerBinderListenerWeakRef: WeakReference? = null - - /** - * Initializes reporting of [WorkerBinderInfo] via [WorkerBinderListener] - * - * IMPORTANT: This should be called only once at early app scope to get proper monitoring of early - * worker being bound - */ - @JvmStatic - public fun initializeMonitoring(workerBinderListener: WorkerBinderListener) { - this.workerBinderListenerWeakRef = WeakReference(workerBinderListener) - } - /** * Bind a worker (ie. a manager or any other class that needs an interactor's lifecycle) to an * interactor's lifecycle events. Inject this class into your interactor and call this method on @@ -81,7 +68,6 @@ public object WorkerBinder { interactor.lifecycleFlow, Interactor.lifecycleRange, dispatcherAtBinder, - workerBinderListenerWeakRef, ) /** @@ -129,7 +115,6 @@ public object WorkerBinder { presenter.lifecycleFlow, Presenter.lifecycleRange, dispatcherAtBinder, - workerBinderListenerWeakRef, ) /** @@ -245,44 +230,6 @@ public object WorkerBinder { } } -/** - * Holds all relevant information for completed Worker.onStart/onStop actions. (e.g. Name of the - * Worker bound, duration of total onStart/onStop, thread name where onStart/onStop happens,etc) - */ -public data class WorkerBinderInfo( - /** Worker class name */ - val workerName: String, - - /** Worker event type (START/STOP) */ - val workerEvent: WorkerEvent, - - /** The [CoroutineContext] where a [Worker] will be bound */ - val coroutineContext: CoroutineContext, - - /** - * Thread name where Worker.onStart/onStop was called. - * - * e.g. When [CoroutineDispatcher] is set as [RibDispatchers.Default] a sample threadName value - * would be similar to `DefaultDispatcher-worker-2` - */ - val threadName: String, - - /** Total binding duration in milliseconds of Worker.onStart/onStop */ - val totalBindingDurationMilli: Long, -) - -/** Reports total binding duration of Worker.onStart/onStop */ -public fun interface WorkerBinderListener { - - /** - * Reports all related Worker information via [WorkerBinderInfo] when onStart/onStop methods are - * completed - */ - public fun onBindCompleted( - workerBinderInfo: WorkerBinderInfo, - ) -} - private fun getJobCoroutineContext( dispatcherAtBinder: CoroutineDispatcher, worker: Worker, @@ -299,7 +246,6 @@ private fun > Worker.bind( lifecycle: SharedFlow, lifecycleRange: ClosedRange, dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, - workerDurationListenerWeakRef: WeakReference?, ): WorkerUnbinder { val coroutineContext = getJobCoroutineContext( @@ -334,9 +280,7 @@ private fun > Worker.bind( .takeWhile { it < lifecycleRange.endInclusive } .onCompletion { bindAndReportWorkerInfo( - workerDurationListenerWeakRef, - WorkerEvent.STOP, - coroutineContext, + RibEventType.DETACHED, ) { onStop() } @@ -344,9 +288,7 @@ private fun > Worker.bind( } .collect { bindAndReportWorkerInfo( - workerDurationListenerWeakRef, - WorkerEvent.START, - coroutineContext, + RibEventType.ATTACHED, ) { onStart(workerScopeProvider) } @@ -356,32 +298,14 @@ private fun > Worker.bind( } private inline fun Worker.bindAndReportWorkerInfo( - workerBinderListeners: WeakReference?, - event: WorkerEvent, - coroutineContext: CoroutineContext, + ribEventType: RibEventType, workerBinderAction: Worker.() -> Unit, ) { - val duration = measureTimeMillis { workerBinderAction() } - workerBinderListeners?.reportWorkerBinderInfo(this, coroutineContext, event, duration) -} - -private fun WeakReference.reportWorkerBinderInfo( - worker: Worker, - coroutineContext: CoroutineContext, - workerEvent: WorkerEvent, - totalBindingEventMilli: Long, -) { - val workerClassName = worker.javaClass.name - val currentThreadName = Thread.currentThread().name - - val workerBinderInfo = - WorkerBinderInfo( - workerClassName, - workerEvent, - coroutineContext, - currentThreadName, - totalBindingEventMilli, - ) - - this@reportWorkerBinderInfo.get()?.onBindCompleted(workerBinderInfo) + val bindActionDurationInMilli = measureTimeMillis { workerBinderAction() } + emitRibEventDuration( + this.javaClass.kotlin, + ribComponentType = RibComponentType.DEPRECATED_WORKER, + ribEventType, + bindActionDurationInMilli, + ) } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 6e489fab0..4faa01c6a 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -22,8 +22,6 @@ import com.uber.rib.core.lifecycle.InteractorEvent import org.junit.Before import org.junit.Test import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -35,7 +33,6 @@ class InteractorAndRouterTest { private val childInteractor: Interactor<*, *> = mock() private val ribRefWatcher: RibRefWatcher = mock() - private val ribMonitoringListener: RibMonitoringListener = mock() private lateinit var interactor: TestInteractor private lateinit var router: TestRouter @@ -71,40 +68,6 @@ class InteractorAndRouterTest { verify(childInteractor).dispatchDetach() } - @Test - fun dispatchAttach_withMonitoringEnabled_shouldReportDuration() { - // Given. - val ribMonitorDataCaptor = argumentCaptor() - RibLogger.initialize(ribMonitoringListener) - - // When. - router.dispatchAttach(null) - - // Then. - verify(ribMonitoringListener, atLeastOnce()).onRibEventCompleted(ribMonitorDataCaptor.capture()) - Truth.assertThat(ribMonitorDataCaptor.firstValue.ribEventMonitorType) - .isEqualTo(RibMonitorType.PRESENTER_DID_LOAD) - Truth.assertThat(ribMonitorDataCaptor.secondValue.ribEventMonitorType) - .isEqualTo(RibMonitorType.INTERACTOR_DID_BECOME_ACTIVE) - } - - @Test - fun dispatchDetach_withMonitoringEnabled_shouldReportDuration() { - // Given. - val ribMonitorDataCaptor = argumentCaptor() - RibLogger.initialize(ribMonitoringListener) - - // When. - router.dispatchDetach() - - // Then. - verify(ribMonitoringListener, atLeastOnce()).onRibEventCompleted(ribMonitorDataCaptor.capture()) - Truth.assertThat(ribMonitorDataCaptor.firstValue.ribEventMonitorType) - .isEqualTo(RibMonitorType.PRESENTER_WILL_UNLOAD) - Truth.assertThat(ribMonitorDataCaptor.secondValue.ribEventMonitorType) - .isEqualTo(RibMonitorType.INTERACTOR_WILL_RESIGN_ACTIVE) - } - @Test @Throws(Exception::class) fun correspondingEvents_whenActive_shouldReturnInactive() { diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 63cc9c7d9..386847938 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -24,12 +24,10 @@ import com.uber.rib.core.WorkerBinder.mapPresenterLifecycleToWorker import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent -import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -54,16 +52,10 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { this } } - private val workerBinderListener: WorkerBinderListener = mock() private val fakeWorker = FakeWorker() private val interactor = FakeInteractor>() - @Before - fun setUp() { - WorkerBinder.initializeMonitoring(workerBinderListener) - } - @Test fun bind_whenInteractorAttached_shouldStartWorker() { val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE) @@ -179,48 +171,28 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Test fun bind_withUnconfinedCoroutineDispatchers_shouldReportBinderInformationForOnStart() = runTest { - val binderDurationCaptor = argumentCaptor() + val ribEventDataCaptor = argumentCaptor() bindFakeWorker() - verify(workerBinderListener).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.firstValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.START, - RibDispatchers.Unconfined, - ) + // TBU } @Test fun bind_multipleWorkers_shouldReportBinderTwice() = runTest { val uiWorker = UiWorker() - val binderDurationCaptor = argumentCaptor() + val ribEventDataCaptor = argumentCaptor() prepareInteractor() val workers = listOf(fakeWorker, fakeWorker, uiWorker) bind(interactor, workers) advanceUntilIdle() - verify(workerBinderListener, times(3)).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.firstValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.START, - RibDispatchers.Unconfined, - ) - binderDurationCaptor.thirdValue.assertWorkerDuration( - "UiWorker", - WorkerEvent.START, - RibDispatchers.Main, - ) + // TBU } @Test fun unbind_withUnconfinedCoroutineDispatchers_shouldReportBinderDurationForOnStop() = runTest { - val binderDurationCaptor = argumentCaptor() + val ribEventDataCaptor = argumentCaptor() val unbinder = bindFakeWorker() unbinder.unbind() - verify(workerBinderListener, times(2)).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.secondValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.STOP, - RibDispatchers.Unconfined, - ) + // TBU } private fun bindFakeWorker(): WorkerUnbinder { @@ -233,14 +205,12 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { interactor.enableTestScopeOverride() } - private fun WorkerBinderInfo.assertWorkerDuration( + private fun RibEventData.assertWorkerDuration( expectedWorkerClassName: String, - expectedWorkerEvent: WorkerEvent, - expectedCoroutineContext: CoroutineContext, + expectedWorkerEvent: RibEventType, ) { - assertThat(workerName).contains(expectedWorkerClassName) - assertThat(workerEvent).isEqualTo(expectedWorkerEvent) - assertThat(expectedCoroutineContext).isEqualTo(expectedCoroutineContext) + assertThat(className).contains(expectedWorkerClassName) + assertThat(ribEventType).isEqualTo(expectedWorkerEvent) } companion object { diff --git a/android/tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreePlugin.kt b/android/tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreePlugin.kt index c5819847a..ebbf40661 100644 --- a/android/tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreePlugin.kt +++ b/android/tooling/rib-flipper-plugin/src/main/kotlin/com/uber/rib/flipper/RibTreePlugin.kt @@ -15,15 +15,13 @@ */ package com.uber.rib.flipper -import android.util.Log -import android.view.View import com.facebook.flipper.core.FlipperConnection import com.facebook.flipper.core.FlipperObject import com.facebook.flipper.core.FlipperPlugin import com.facebook.flipper.core.FlipperResponder import com.uber.rib.core.RibDebugOverlay -import com.uber.rib.core.RibEvent import com.uber.rib.core.RibEvents +import com.uber.rib.core.RibRouterEvent import com.uber.rib.core.Router import com.uber.rib.core.ViewRouter import com.uber.rib.flipper.RibEventPayload.Companion.EVENT_PARAMETER_ID @@ -54,10 +52,9 @@ class RibTreePlugin : FlipperPlugin { init { // Start listening to rib events right away, since flipper client might connect only later on - RibEvents.getInstance() - .events - .filter { e: RibEvent -> e.parentRouter != null } - .map { e: RibEvent -> + RibEvents.routerEvents + .filter { e: RibRouterEvent -> e.parentRouter != null } + .map { e: RibRouterEvent -> val router: Router<*> = e.router val routerId = createRouterIdIfNeeded(router) val parentRouter: Router<*>? = e.parentRouter diff --git a/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyDebugBroadcastHandler.java b/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyDebugBroadcastHandler.java index 6ced435a5..382ba088d 100644 --- a/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyDebugBroadcastHandler.java +++ b/android/tooling/rib-intellij-plugin/native/intellij-broadcast-rib/src/main/java/com/uber/debug/broadcast/rib/RibHierarchyDebugBroadcastHandler.java @@ -35,7 +35,7 @@ import com.uber.debug.broadcast.rib.RibHierarchyPayload.RibNode; import com.uber.debug.broadcast.rib.RibHierarchyPayload.RibView; import com.uber.rib.core.RibDebugOverlay; -import com.uber.rib.core.RibEvent; +import com.uber.rib.core.RibRouterEvent; import com.uber.rib.core.Router; import com.uber.rib.core.ViewRouter; import io.reactivex.Observable; @@ -54,7 +54,7 @@ * Debug broadcast handler responsible for exposing RIB hierarchy. */ public class RibHierarchyDebugBroadcastHandler - implements Observer, Handler { + implements Observer, Handler { public static final String COMMAND_RIB_HIERARCHY = "RIB_HIERARCHY"; public static final String COMMAND_RIB_HIGHLIGHT = "RIB_HIGHLIGHT"; @@ -80,7 +80,8 @@ public class RibHierarchyDebugBroadcastHandler private @Nullable DebugBroadcastRequest mPendingLocateRequest = null; @SuppressWarnings("RxJavaSubscribeInConstructor") - public RibHierarchyDebugBroadcastHandler(Context context, Observable ribEventsStream) { + public RibHierarchyDebugBroadcastHandler( + Context context, Observable ribEventsStream) { this.processName = getCurrentProcessName(context); ribEventsStream.subscribe(this); } @@ -120,16 +121,16 @@ public void handle(DebugBroadcastRequest request) { public void onSubscribe(Disposable d) {} @Override - public void onNext(RibEvent ribEvent) { - Router childRouter = ribEvent.getRouter(); - Router parentRouter = ribEvent.getParentRouter(); + public void onNext(RibRouterEvent ribRouterEvent) { + Router childRouter = ribRouterEvent.getRouter(); + Router parentRouter = ribRouterEvent.getParentRouter(); if (parentRouter == null) { return; } UUID childId = createRouterIdIfNeeded(childRouter); UUID parentId = createRouterIdIfNeeded(parentRouter); try { - switch (ribEvent.getEventType()) { + switch (ribRouterEvent.getEventType()) { case ATTACHED: addChild(parentId, childId); break; @@ -137,14 +138,15 @@ public void onNext(RibEvent ribEvent) { removeChild(parentId, childId); break; default: - throw new UnsupportedOperationException("Unknown command: " + ribEvent.getEventType()); + throw new UnsupportedOperationException( + "Unknown command: " + ribRouterEvent.getEventType()); } } catch (IllegalArgumentException e) { String message = String.format( Locale.US, "Error processing RibEvent %s: parent=%s child=%s", - ribEvent.getEventType().toString(), + ribRouterEvent.getEventType().toString(), parentRouter.getClass().getSimpleName(), childRouter.getClass().getSimpleName()); Log.w(TAG, message); From 5ff47487df48b63c442f2471b968fbf83b3a9d1f Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Fri, 16 Jun 2023 16:16:11 -0700 Subject: [PATCH 14/52] Drafting more ideas --- .../uber/rib/workers/ComposeApplication.kt | 10 +-- .../logger/ApplicationLevelWorkerLogger.kt | 78 +++++++++++++++++++ .../rib/workers/root/logger/RibEventLogger.kt | 43 ---------- .../kotlin/com/uber/rib/core/Interactor.kt | 45 +++++------ .../kotlin/com/uber/rib/core/RibEvents.kt | 55 ++++++++----- .../main/kotlin/com/uber/rib/core/Router.kt | 30 +++---- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 17 ++-- 7 files changed, 163 insertions(+), 115 deletions(-) create mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt delete mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt index 1696afdaf..ad7c8a617 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt @@ -22,17 +22,11 @@ import com.facebook.flipper.core.FlipperClient import com.facebook.flipper.plugins.inspector.DescriptorMapping import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin import com.facebook.soloader.SoLoader -import com.uber.rib.core.RibEvents import com.uber.rib.flipper.RibTreePlugin -import com.uber.rib.workers.root.logger.RibEventLogger -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.rx2.asFlow +import com.uber.rib.workers.root.logger.ApplicationLevelWorkerLogger class ComposeApplication : Application() { - @OptIn(DelicateCoroutinesApi::class) override fun onCreate() { super.onCreate() SoLoader.init(this, false) @@ -44,6 +38,6 @@ class ComposeApplication : Application() { client.start() } - GlobalScope.launch { RibEvents.ribDurationEvents.asFlow().collect { RibEventLogger.log(it) } } + ApplicationLevelWorkerLogger.start() } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt new file mode 100644 index 000000000..2cb7bf7c8 --- /dev/null +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -0,0 +1,78 @@ +/* + * 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.workers.root.logger + +import android.util.Log +import com.uber.rib.core.RibActionInfo +import com.uber.rib.core.RibActionType +import com.uber.rib.core.RibComponentType +import com.uber.rib.core.RibDispatchers +import com.uber.rib.core.RibEvents +import java.lang.System.currentTimeMillis +import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.rx2.asFlow + +/** + * Sample of consuming [ribActionEvents] possibilities. + * 1. Can pipe Interactor/Router/Presenter/Worker information to backend + * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection + * 3. More tailored aggregation if needed. + * + * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker + * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance + */ +object ApplicationLevelWorkerLogger { + private const val LOG_TAG = "RibEventLogger" + + private val concurrentHashMap: ConcurrentHashMap = ConcurrentHashMap() + + @OptIn(DelicateCoroutinesApi::class) + fun start() { + GlobalScope.launch(RibDispatchers.Unconfined) { + RibEvents.ribActionEvents + .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } + .asFlow() + .collect { it.logWorkerDuration() } + } + } + + private fun RibActionInfo.logWorkerDuration() { + val ribComponentKey = this.className + if (ribActionType == RibActionType.STARTED && !ribComponentKey.isClassNameInMap()) { + concurrentHashMap[ribComponentKey] = currentTimeMillis() + } + + if (ribActionType == RibActionType.COMPLETED && ribComponentKey.isClassNameInMap()) { + val preOnStartDuration = concurrentHashMap[ribComponentKey] + preOnStartDuration?.let { className.logDuration(it) } + concurrentHashMap.remove(ribComponentKey) + } + } + + private fun String.isClassNameInMap(): Boolean = concurrentHashMap.containsKey(this) + + private fun String.logDuration(preOnStartDuration: Long) { + val totalDuration = currentTimeMillis() - preOnStartDuration + val currentThreadName = Thread.currentThread().name + Log.d( + LOG_TAG, + "WORKER_BINDING_INFO -> $this.onStart() took $totalDuration ms on $currentThreadName thread", + ) + } +} diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt deleted file mode 100644 index af897b9db..000000000 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.workers.root.logger - -import android.util.Log -import com.uber.rib.core.RibEventDurationData -import com.uber.rib.workers.root.logger.RibEventLogger.log - -/** - * Sample of consuming [ribEventDataFlow] possibilities. - * 1. Can pipe Interactor/Router/Presenter/Worker information to backend - * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection - * 3. More tailored aggregation if needed. - * - * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker - * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance - */ -object RibEventLogger { - - private const val LOG_TAG = "RibEventLogger" - - fun log(ribEventData: RibEventDurationData) { - val message = ribEventData.buildWorkerDurationMessage() - Log.d(LOG_TAG, message) - } - - private fun RibEventDurationData.buildWorkerDurationMessage(): String { - return "RibEventDurationData: ${this.ribComponentType}_${this.ribEventType} at ${this.className} took ${this.totalBindingDurationMilli} ms in thread: ${this.threadName}" - } -} 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 9a75d55e0..525814c86 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 @@ -19,14 +19,13 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException -import com.uber.rib.core.RibEvents.emitRibEventDuration +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable import javax.inject.Inject import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -import kotlin.system.measureTimeMillis import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -107,45 +106,43 @@ public abstract class Interactor

>() : InteractorType { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidLoadDuration = measureTimeMillis { it.dispatchLoad() } - emitRibEventDuration( + callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.ATTACHED, - presenterDidLoadDuration, - ) + RibEventType.ATTACHED + ) { + it.dispatchLoad() + } } - val interactorDidBecomeActiveDuration = measureTimeMillis { - didBecomeActive(savedInstanceState) - } - emitRibEventDuration( + callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.ATTACHED, - interactorDidBecomeActiveDuration, - ) + RibEventType.ATTACHED + ) { + didBecomeActive(savedInstanceState) + } } public open fun dispatchDetach(): P { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidUnloadDuration = measureTimeMillis { it.dispatchUnload() } - emitRibEventDuration( + callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.DETACHED, - presenterDidUnloadDuration, - ) + RibEventType.DETACHED + ) { + it.dispatchUnload() + } } - val interactorWillResignActiveDuration = measureTimeMillis { willResignActive() } - emitRibEventDuration( + callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.DETACHED, - interactorWillResignActiveDuration, - ) + RibEventType.DETACHED + ) { + willResignActive() + } _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 446c1751a..b9b0435f3 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -26,15 +26,13 @@ public object RibEvents { private val mutableRouterEvents = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) private val mutableRibDurationEvents = - MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) + MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) @JvmStatic - public val routerEvents: Observable - get() = mutableRouterEvents.asObservable() + public val routerEvents: Observable = mutableRouterEvents.asObservable() @JvmStatic - public val ribDurationEvents: Observable - get() = mutableRibDurationEvents.asObservable() + public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() /** * @param eventType [RibEventType] @@ -46,6 +44,26 @@ public object RibEvents { mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent)) } + /** + * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events + * for each RIB component. + * + * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, + * UiRibWorker, etc) + * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) + * @param ribEventType RIB event type (e.g. ATTACH/DETACH) + */ + internal fun callRibActionAndEmitEvents( + ribClass: KClass<*>, + ribComponentType: RibComponentType, + ribEventType: RibEventType, + ribAction: () -> Unit, + ) { + emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.STARTED) + ribAction() + emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.COMPLETED) + } + /** * Emits emission of ATTACHED/DETACHED events for each RIB component. * @@ -53,45 +71,44 @@ public object RibEvents { * UiRibWorker, etc) * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) - * @param totalBindingDurationMilli Total duration (in ms) of each ATTACH/DETACH events + * @param ribActionType: RibActionType, */ - internal fun emitRibEventDuration( + private fun emitRibEventAction( ribClass: KClass<*>, ribComponentType: RibComponentType, ribEventType: RibEventType, - totalBindingDurationMilli: Long, + ribActionType: RibActionType, ) { val ribClassName = ribClass.qualifiedName // There's no point to emit emission if we don't know which RIB component name was triggered ribClassName?.let { val ribEventData = - RibEventDurationData( + RibActionInfo( it, ribComponentType, ribEventType, - Thread.currentThread().name, - totalBindingDurationMilli, + ribActionType, ) mutableRibDurationEvents.tryEmit(ribEventData) } } } +public enum class RibActionType { + STARTED, + COMPLETED, +} + /** Holds relevant RIB event information */ -public data class RibEventDurationData( +public data class RibActionInfo( /** Related RIB class name */ val className: String, /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ val ribComponentType: RibComponentType, - - /** RIB component event type ATTACHED/DETACHED */ val ribEventType: RibEventType, - /** Reports the current thread name where Rib Event happen (should mainly be main thread) */ - val threadName: String, - - /** Total binding duration in milliseconds of Worker.onStart/onStop */ - val totalBindingDurationMilli: Long, + /** RIB component event type PRE_ATTACH */ + val ribActionType: RibActionType, ) 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 70760477b..2130c9b20 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 @@ -19,9 +19,9 @@ import android.os.Looper import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList -import kotlin.system.measureTimeMillis /** * Responsible for handling the addition and removal of children routers. @@ -114,13 +114,15 @@ protected constructor( ) } } - val attachedRouterDurationInMilli = measureTimeMillis { children.add(childRouter) } - RibEvents.emitRibEventDuration( - this.javaClass.kotlin, + + callRibActionAndEmitEvents( + childRouter.javaClass.kotlin, RibComponentType.ROUTER, RibEventType.ATTACHED, - attachedRouterDurationInMilli, - ) + ) { + children.add(childRouter) + } + ribRefWatcher.logBreadcrumb( "ATTACHED", childRouter.javaClass.simpleName, @@ -164,14 +166,16 @@ protected constructor( .handleNonFatalWarning("A RIB tried to detach a child that was never attached", null) } } - val routerDetachDurationInMilli = measureTimeMillis { childRouter.dispatchDetach() } + + callRibActionAndEmitEvents( + childRouter.javaClass.kotlin, + RibComponentType.ROUTER, + RibEventType.DETACHED, + ) { + childRouter.dispatchDetach() + } + if (isChildRemoved) { - RibEvents.emitRibEventDuration( - this.javaClass.kotlin, - RibComponentType.ROUTER, - RibEventType.DETACHED, - routerDetachDurationInMilli, - ) RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) } } 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 5a5e139b0..afe236e4a 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 @@ -18,7 +18,7 @@ package com.uber.rib.core import androidx.annotation.VisibleForTesting import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider -import com.uber.rib.core.RibEvents.emitRibEventDuration +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent @@ -26,7 +26,6 @@ import io.reactivex.Observable import io.reactivex.subjects.CompletableSubject import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.system.measureTimeMillis import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.DelicateCoroutinesApi @@ -299,13 +298,15 @@ private fun > Worker.bind( private inline fun Worker.bindAndReportWorkerInfo( ribEventType: RibEventType, - workerBinderAction: Worker.() -> Unit, + crossinline workerBinderAction: Worker.() -> Unit, ) { - val bindActionDurationInMilli = measureTimeMillis { workerBinderAction() } - emitRibEventDuration( + val workerClass = this.javaClass.kotlin + + callRibActionAndEmitEvents( this.javaClass.kotlin, - ribComponentType = RibComponentType.DEPRECATED_WORKER, + RibComponentType.DEPRECATED_WORKER, ribEventType, - bindActionDurationInMilli, - ) + ) { + workerBinderAction() + } } From 3ab395548eae3c5605f7735c937dce095a557090 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Fri, 16 Jun 2023 16:16:11 -0700 Subject: [PATCH 15/52] Drafting more ideas --- .../uber/rib/workers/ComposeApplication.kt | 10 +-- .../logger/ApplicationLevelWorkerLogger.kt | 78 +++++++++++++++++++ .../rib/workers/root/logger/RibEventLogger.kt | 43 ---------- .../kotlin/com/uber/rib/core/Interactor.kt | 45 +++++------ .../kotlin/com/uber/rib/core/RibEvents.kt | 55 ++++++++----- .../main/kotlin/com/uber/rib/core/Router.kt | 30 +++---- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 17 ++-- 7 files changed, 163 insertions(+), 115 deletions(-) create mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt delete mode 100644 android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt index 1696afdaf..ad7c8a617 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/ComposeApplication.kt @@ -22,17 +22,11 @@ import com.facebook.flipper.core.FlipperClient import com.facebook.flipper.plugins.inspector.DescriptorMapping import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin import com.facebook.soloader.SoLoader -import com.uber.rib.core.RibEvents import com.uber.rib.flipper.RibTreePlugin -import com.uber.rib.workers.root.logger.RibEventLogger -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.rx2.asFlow +import com.uber.rib.workers.root.logger.ApplicationLevelWorkerLogger class ComposeApplication : Application() { - @OptIn(DelicateCoroutinesApi::class) override fun onCreate() { super.onCreate() SoLoader.init(this, false) @@ -44,6 +38,6 @@ class ComposeApplication : Application() { client.start() } - GlobalScope.launch { RibEvents.ribDurationEvents.asFlow().collect { RibEventLogger.log(it) } } + ApplicationLevelWorkerLogger.start() } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt new file mode 100644 index 000000000..3dafb5f3c --- /dev/null +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -0,0 +1,78 @@ +/* + * 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.workers.root.logger + +import android.util.Log +import com.uber.rib.core.RibActionInfo +import com.uber.rib.core.RibActionType +import com.uber.rib.core.RibComponentType +import com.uber.rib.core.RibDispatchers +import com.uber.rib.core.RibEvents +import java.lang.System.currentTimeMillis +import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.rx2.asFlow + +/** + * Sample of consuming [ribActionEvents] possibilities. + * 1. Can pipe Interactor/Router/Presenter/Worker information to backend + * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection + * 3. More tailored aggregation if needed. + * + * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker + * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance + */ +object ApplicationLevelWorkerLogger { + private const val LOG_TAG = "RibEventLogger" + + private val concurrentHashMap: ConcurrentHashMap = ConcurrentHashMap() + + @OptIn(DelicateCoroutinesApi::class) + fun start() { + GlobalScope.launch(RibDispatchers.Unconfined) { + RibEvents.ribActionEvents + .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } + .asFlow() + .collect { it.logWorkerDuration() } + } + } + + private fun RibActionInfo.logWorkerDuration() { + val ribComponentKey = this.className + if (ribActionType == RibActionType.STARTED && !ribComponentKey.isClassNameInMap()) { + concurrentHashMap[ribComponentKey] = currentTimeMillis() + } + + if (ribActionType == RibActionType.COMPLETED && ribComponentKey.isClassNameInMap()) { + val preOnStartDuration = concurrentHashMap[ribComponentKey] + preOnStartDuration?.let { this.logDuration(it) } + concurrentHashMap.remove(ribComponentKey) + } + } + + private fun String.isClassNameInMap(): Boolean = concurrentHashMap.containsKey(this) + + private fun RibActionInfo.logDuration(preOnStartDuration: Long) { + val totalDuration = currentTimeMillis() - preOnStartDuration + val currentThreadName = Thread.currentThread().name + Log.d( + LOG_TAG, + "WORKER_BINDING_INFO -> ${this.className} ${this.ribEventType} took $totalDuration ms on $currentThreadName thread", + ) + } +} diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt deleted file mode 100644 index af897b9db..000000000 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/RibEventLogger.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.workers.root.logger - -import android.util.Log -import com.uber.rib.core.RibEventDurationData -import com.uber.rib.workers.root.logger.RibEventLogger.log - -/** - * Sample of consuming [ribEventDataFlow] possibilities. - * 1. Can pipe Interactor/Router/Presenter/Worker information to backend - * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection - * 3. More tailored aggregation if needed. - * - * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker - * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance - */ -object RibEventLogger { - - private const val LOG_TAG = "RibEventLogger" - - fun log(ribEventData: RibEventDurationData) { - val message = ribEventData.buildWorkerDurationMessage() - Log.d(LOG_TAG, message) - } - - private fun RibEventDurationData.buildWorkerDurationMessage(): String { - return "RibEventDurationData: ${this.ribComponentType}_${this.ribEventType} at ${this.className} took ${this.totalBindingDurationMilli} ms in thread: ${this.threadName}" - } -} 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 9a75d55e0..525814c86 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 @@ -19,14 +19,13 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException -import com.uber.rib.core.RibEvents.emitRibEventDuration +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable import javax.inject.Inject import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -import kotlin.system.measureTimeMillis import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -107,45 +106,43 @@ public abstract class Interactor

>() : InteractorType { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidLoadDuration = measureTimeMillis { it.dispatchLoad() } - emitRibEventDuration( + callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.ATTACHED, - presenterDidLoadDuration, - ) + RibEventType.ATTACHED + ) { + it.dispatchLoad() + } } - val interactorDidBecomeActiveDuration = measureTimeMillis { - didBecomeActive(savedInstanceState) - } - emitRibEventDuration( + callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.ATTACHED, - interactorDidBecomeActiveDuration, - ) + RibEventType.ATTACHED + ) { + didBecomeActive(savedInstanceState) + } } public open fun dispatchDetach(): P { val presenter = (getPresenter() as? Presenter) presenter?.let { - val presenterDidUnloadDuration = measureTimeMillis { it.dispatchUnload() } - emitRibEventDuration( + callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.DETACHED, - presenterDidUnloadDuration, - ) + RibEventType.DETACHED + ) { + it.dispatchUnload() + } } - val interactorWillResignActiveDuration = measureTimeMillis { willResignActive() } - emitRibEventDuration( + callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.DETACHED, - interactorWillResignActiveDuration, - ) + RibEventType.DETACHED + ) { + willResignActive() + } _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 446c1751a..b9b0435f3 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -26,15 +26,13 @@ public object RibEvents { private val mutableRouterEvents = MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) private val mutableRibDurationEvents = - MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) + MutableSharedFlow(0, 1, BufferOverflow.DROP_OLDEST) @JvmStatic - public val routerEvents: Observable - get() = mutableRouterEvents.asObservable() + public val routerEvents: Observable = mutableRouterEvents.asObservable() @JvmStatic - public val ribDurationEvents: Observable - get() = mutableRibDurationEvents.asObservable() + public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() /** * @param eventType [RibEventType] @@ -46,6 +44,26 @@ public object RibEvents { mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent)) } + /** + * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events + * for each RIB component. + * + * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, + * UiRibWorker, etc) + * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) + * @param ribEventType RIB event type (e.g. ATTACH/DETACH) + */ + internal fun callRibActionAndEmitEvents( + ribClass: KClass<*>, + ribComponentType: RibComponentType, + ribEventType: RibEventType, + ribAction: () -> Unit, + ) { + emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.STARTED) + ribAction() + emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.COMPLETED) + } + /** * Emits emission of ATTACHED/DETACHED events for each RIB component. * @@ -53,45 +71,44 @@ public object RibEvents { * UiRibWorker, etc) * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) - * @param totalBindingDurationMilli Total duration (in ms) of each ATTACH/DETACH events + * @param ribActionType: RibActionType, */ - internal fun emitRibEventDuration( + private fun emitRibEventAction( ribClass: KClass<*>, ribComponentType: RibComponentType, ribEventType: RibEventType, - totalBindingDurationMilli: Long, + ribActionType: RibActionType, ) { val ribClassName = ribClass.qualifiedName // There's no point to emit emission if we don't know which RIB component name was triggered ribClassName?.let { val ribEventData = - RibEventDurationData( + RibActionInfo( it, ribComponentType, ribEventType, - Thread.currentThread().name, - totalBindingDurationMilli, + ribActionType, ) mutableRibDurationEvents.tryEmit(ribEventData) } } } +public enum class RibActionType { + STARTED, + COMPLETED, +} + /** Holds relevant RIB event information */ -public data class RibEventDurationData( +public data class RibActionInfo( /** Related RIB class name */ val className: String, /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ val ribComponentType: RibComponentType, - - /** RIB component event type ATTACHED/DETACHED */ val ribEventType: RibEventType, - /** Reports the current thread name where Rib Event happen (should mainly be main thread) */ - val threadName: String, - - /** Total binding duration in milliseconds of Worker.onStart/onStop */ - val totalBindingDurationMilli: Long, + /** RIB component event type PRE_ATTACH */ + val ribActionType: RibActionType, ) 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 70760477b..2130c9b20 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 @@ -19,9 +19,9 @@ import android.os.Looper import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList -import kotlin.system.measureTimeMillis /** * Responsible for handling the addition and removal of children routers. @@ -114,13 +114,15 @@ protected constructor( ) } } - val attachedRouterDurationInMilli = measureTimeMillis { children.add(childRouter) } - RibEvents.emitRibEventDuration( - this.javaClass.kotlin, + + callRibActionAndEmitEvents( + childRouter.javaClass.kotlin, RibComponentType.ROUTER, RibEventType.ATTACHED, - attachedRouterDurationInMilli, - ) + ) { + children.add(childRouter) + } + ribRefWatcher.logBreadcrumb( "ATTACHED", childRouter.javaClass.simpleName, @@ -164,14 +166,16 @@ protected constructor( .handleNonFatalWarning("A RIB tried to detach a child that was never attached", null) } } - val routerDetachDurationInMilli = measureTimeMillis { childRouter.dispatchDetach() } + + callRibActionAndEmitEvents( + childRouter.javaClass.kotlin, + RibComponentType.ROUTER, + RibEventType.DETACHED, + ) { + childRouter.dispatchDetach() + } + if (isChildRemoved) { - RibEvents.emitRibEventDuration( - this.javaClass.kotlin, - RibComponentType.ROUTER, - RibEventType.DETACHED, - routerDetachDurationInMilli, - ) RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) } } 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 5a5e139b0..afe236e4a 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 @@ -18,7 +18,7 @@ package com.uber.rib.core import androidx.annotation.VisibleForTesting import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider -import com.uber.rib.core.RibEvents.emitRibEventDuration +import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent @@ -26,7 +26,6 @@ import io.reactivex.Observable import io.reactivex.subjects.CompletableSubject import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.system.measureTimeMillis import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.DelicateCoroutinesApi @@ -299,13 +298,15 @@ private fun > Worker.bind( private inline fun Worker.bindAndReportWorkerInfo( ribEventType: RibEventType, - workerBinderAction: Worker.() -> Unit, + crossinline workerBinderAction: Worker.() -> Unit, ) { - val bindActionDurationInMilli = measureTimeMillis { workerBinderAction() } - emitRibEventDuration( + val workerClass = this.javaClass.kotlin + + callRibActionAndEmitEvents( this.javaClass.kotlin, - ribComponentType = RibComponentType.DEPRECATED_WORKER, + RibComponentType.DEPRECATED_WORKER, ribEventType, - bindActionDurationInMilli, - ) + ) { + workerBinderAction() + } } From 80c5846f46055cf593ea804223a3425ea0883617 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Tue, 20 Jun 2023 12:51:37 -0700 Subject: [PATCH 16/52] spotless apply --- .../kotlin/com/uber/rib/core/Interactor.kt | 8 ++-- .../kotlin/com/uber/rib/core/RibEvents.kt | 3 +- .../uber/rib/core/InteractorAndRouterTest.kt | 44 +++++++++++++++++++ .../com/uber/rib/core/WorkerBinderTest.kt | 42 +++++++++++------- 4 files changed, 77 insertions(+), 20 deletions(-) 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 525814c86..d36d67b32 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 @@ -109,7 +109,7 @@ public abstract class Interactor

>() : InteractorType { callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.ATTACHED + RibEventType.ATTACHED, ) { it.dispatchLoad() } @@ -118,7 +118,7 @@ public abstract class Interactor

>() : InteractorType { callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.ATTACHED + RibEventType.ATTACHED, ) { didBecomeActive(savedInstanceState) } @@ -130,7 +130,7 @@ public abstract class Interactor

>() : InteractorType { callRibActionAndEmitEvents( it.javaClass.kotlin, RibComponentType.PRESENTER, - RibEventType.DETACHED + RibEventType.DETACHED, ) { it.dispatchUnload() } @@ -139,7 +139,7 @@ public abstract class Interactor

>() : InteractorType { callRibActionAndEmitEvents( this.javaClass.kotlin, RibComponentType.INTERACTOR, - RibEventType.DETACHED + RibEventType.DETACHED, ) { willResignActive() } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index b9b0435f3..f443c5dd4 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -50,8 +50,9 @@ public object RibEvents { * * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, * UiRibWorker, etc) - * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) + * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) + * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc */ internal fun callRibActionAndEmitEvents( ribClass: KClass<*>, diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 4faa01c6a..7e5ce42b2 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -17,8 +17,10 @@ package com.uber.rib.core import com.google.common.truth.Truth import com.uber.autodispose.lifecycle.LifecycleEndedException +import com.uber.rib.core.RibEvents.ribActionEvents import com.uber.rib.core.RibRefWatcher.Companion.getInstance import com.uber.rib.core.lifecycle.InteractorEvent +import io.reactivex.observers.TestObserver import org.junit.Before import org.junit.Test import org.mockito.kotlin.any @@ -36,6 +38,7 @@ class InteractorAndRouterTest { private lateinit var interactor: TestInteractor private lateinit var router: TestRouter + private val ribActionInfoObserver = TestObserver() @Before fun setup() { @@ -49,22 +52,52 @@ class InteractorAndRouterTest { @Test fun attach_shouldAttachChildController() { + // Given. + ribActionEvents.subscribe(ribActionInfoObserver) + // When. router.dispatchAttach(null) // Then. + val ribActionInfoValues = ribActionInfoObserver.values() + Truth.assertThat( + ribActionInfoValues.contains( + buildInteractorAction(RibEventType.ATTACHED, RibActionType.STARTED), + ), + ) + .isTrue() + Truth.assertThat( + ribActionInfoValues.contains( + buildInteractorAction(RibEventType.ATTACHED, RibActionType.COMPLETED), + ), + ) + .isTrue() verify(childInteractor).dispatchAttach(null) } @Test fun detach_shouldDetachChildController() { // Given. + ribActionEvents.subscribe(ribActionInfoObserver) router.dispatchAttach(null) // When. router.dispatchDetach() // Then. + val ribActionInfoValues = ribActionInfoObserver.values() + Truth.assertThat( + ribActionInfoValues.contains( + buildInteractorAction(RibEventType.DETACHED, RibActionType.STARTED), + ), + ) + .isTrue() + Truth.assertThat( + ribActionInfoValues.contains( + buildInteractorAction(RibEventType.DETACHED, RibActionType.COMPLETED), + ), + ) + .isTrue() verify(childInteractor).dispatchDetach() } @@ -262,6 +295,17 @@ class InteractorAndRouterTest { } } + private fun buildInteractorAction( + ribEventType: RibEventType, + ribActionType: RibActionType, + ) = + RibActionInfo( + "com.uber.rib.core.InteractorAndRouterTest.TestInteractor", + ribComponentType = RibComponentType.INTERACTOR, + ribEventType = ribEventType, + ribActionType = ribActionType, + ) + companion object { private const val TEST_KEY = "test_key" private const val TEST_VALUE = "test_value" diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 386847938..67dca9e0d 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -17,6 +17,7 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat import com.jakewharton.rxrelay2.BehaviorRelay +import com.uber.rib.core.RibEvents.ribActionEvents import com.uber.rib.core.WorkerBinder.bind import com.uber.rib.core.WorkerBinder.bindToWorkerLifecycle import com.uber.rib.core.WorkerBinder.mapInteractorLifecycleToWorker @@ -24,6 +25,7 @@ import com.uber.rib.core.WorkerBinder.mapPresenterLifecycleToWorker import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent +import io.reactivex.observers.TestObserver import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle @@ -33,7 +35,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times @@ -55,6 +56,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { private val fakeWorker = FakeWorker() private val interactor = FakeInteractor>() + private val ribActionInfoObserver = TestObserver() @Test fun bind_whenInteractorAttached_shouldStartWorker() { @@ -171,28 +173,35 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Test fun bind_withUnconfinedCoroutineDispatchers_shouldReportBinderInformationForOnStart() = runTest { - val ribEventDataCaptor = argumentCaptor() + val expectedRibActionInfo = buildExpectedWorkerRibAction() + ribActionEvents.subscribe(ribActionInfoObserver) bindFakeWorker() - // TBU + val ribActionInfoValues = ribActionInfoObserver.values() + assertThat(ribActionInfoValues.last()).isEqualTo(expectedRibActionInfo) } @Test - fun bind_multipleWorkers_shouldReportBinderTwice() = runTest { + fun bind_multipleWorkers_shouldReportBinderUiWorker() = runTest { + val expectedUiRibActionEvent = buildExpectedWorkerRibAction("com.uber.rib.core.UiWorker") + ribActionEvents.subscribe(ribActionInfoObserver) val uiWorker = UiWorker() - val ribEventDataCaptor = argumentCaptor() prepareInteractor() val workers = listOf(fakeWorker, fakeWorker, uiWorker) bind(interactor, workers) advanceUntilIdle() - // TBU + val ribActionInfoValues = ribActionInfoObserver.values() + assertThat(ribActionInfoValues.last()).isEqualTo(expectedUiRibActionEvent) } @Test fun unbind_withUnconfinedCoroutineDispatchers_shouldReportBinderDurationForOnStop() = runTest { - val ribEventDataCaptor = argumentCaptor() + val expectedUiRibActionEvent = + buildExpectedWorkerRibAction(ribEventType = RibEventType.DETACHED) + ribActionEvents.subscribe(ribActionInfoObserver) val unbinder = bindFakeWorker() unbinder.unbind() - // TBU + val ribActionInfoValues = ribActionInfoObserver.values() + assertThat(ribActionInfoValues.last()).isEqualTo(expectedUiRibActionEvent) } private fun bindFakeWorker(): WorkerUnbinder { @@ -205,13 +214,16 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { interactor.enableTestScopeOverride() } - private fun RibEventData.assertWorkerDuration( - expectedWorkerClassName: String, - expectedWorkerEvent: RibEventType, - ) { - assertThat(className).contains(expectedWorkerClassName) - assertThat(ribEventType).isEqualTo(expectedWorkerEvent) - } + private fun buildExpectedWorkerRibAction( + expectedClassName: String = "com.uber.rib.core.FakeWorker", + ribEventType: RibEventType = RibEventType.ATTACHED, + ): RibActionInfo = + RibActionInfo( + expectedClassName, + RibComponentType.DEPRECATED_WORKER, + ribEventType, + RibActionType.COMPLETED, + ) companion object { @JvmStatic From 38ae582f9532ed486ddcaea7dcca647c127ba0c7 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 00:22:32 -0700 Subject: [PATCH 17/52] Rename callRibActionAndEmitEvents to triggerRibActionAndEmitEvents --- .../logger/ApplicationLevelWorkerLogger.kt | 6 +- .../kotlin/com/uber/rib/core/Interactor.kt | 39 +++++------ .../kotlin/com/uber/rib/core/RibEvents.kt | 66 +++++++++---------- .../main/kotlin/com/uber/rib/core/Router.kt | 20 +++--- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 35 ++++------ .../uber/rib/core/InteractorAndRouterTest.kt | 12 ++-- .../com/uber/rib/core/WorkerBinderTest.kt | 2 +- 7 files changed, 78 insertions(+), 102 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index 3dafb5f3c..51e9622a5 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -17,7 +17,7 @@ package com.uber.rib.workers.root.logger import android.util.Log import com.uber.rib.core.RibActionInfo -import com.uber.rib.core.RibActionType +import com.uber.rib.core.RibActionState import com.uber.rib.core.RibComponentType import com.uber.rib.core.RibDispatchers import com.uber.rib.core.RibEvents @@ -54,11 +54,11 @@ object ApplicationLevelWorkerLogger { private fun RibActionInfo.logWorkerDuration() { val ribComponentKey = this.className - if (ribActionType == RibActionType.STARTED && !ribComponentKey.isClassNameInMap()) { + if (ribActionState == RibActionState.STARTED && !ribComponentKey.isClassNameInMap()) { concurrentHashMap[ribComponentKey] = currentTimeMillis() } - if (ribActionType == RibActionType.COMPLETED && ribComponentKey.isClassNameInMap()) { + if (ribActionState == RibActionState.COMPLETED && ribComponentKey.isClassNameInMap()) { val preOnStartDuration = concurrentHashMap[ribComponentKey] preOnStartDuration?.let { this.logDuration(it) } concurrentHashMap.remove(ribComponentKey) 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 d36d67b32..dd6865121 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 @@ -19,7 +19,7 @@ import androidx.annotation.CallSuper import androidx.annotation.VisibleForTesting import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException -import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents +import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -106,44 +106,39 @@ public abstract class Interactor

>() : InteractorType { val presenter = (getPresenter() as? Presenter) presenter?.let { - callRibActionAndEmitEvents( - it.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { it.dispatchLoad() }, + it, RibComponentType.PRESENTER, RibEventType.ATTACHED, - ) { - it.dispatchLoad() - } + ) } - callRibActionAndEmitEvents( - this.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { didBecomeActive(savedInstanceState) }, + this, RibComponentType.INTERACTOR, RibEventType.ATTACHED, - ) { - didBecomeActive(savedInstanceState) - } + ) } public open fun dispatchDetach(): P { val presenter = (getPresenter() as? Presenter) presenter?.let { - callRibActionAndEmitEvents( - it.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { it.dispatchUnload() }, + it, RibComponentType.PRESENTER, RibEventType.DETACHED, - ) { - it.dispatchUnload() - } + ) } - callRibActionAndEmitEvents( - this.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { willResignActive() }, + this, RibComponentType.INTERACTOR, RibEventType.DETACHED, - ) { - willResignActive() - } - + ) _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) return getPresenter() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index f443c5dd4..665d470a2 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -16,7 +16,6 @@ package com.uber.rib.core import io.reactivex.Observable -import kotlin.reflect.KClass import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.rx2.asObservable @@ -48,59 +47,48 @@ public object RibEvents { * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events * for each RIB component. * - * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, - * UiRibWorker, etc) + * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc + * @param ribCallerClassType Related RIB component class type * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) - * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc */ - internal fun callRibActionAndEmitEvents( - ribClass: KClass<*>, + internal fun triggerRibActionAndEmitEvents( + ribAction: () -> Unit, + ribCallerClassType: T, ribComponentType: RibComponentType, ribEventType: RibEventType, - ribAction: () -> Unit, ) { - emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.STARTED) + val ribClassName = ribCallerClassType.asQualifiedName() + ribClassName?.emitRibEventAction(ribComponentType, ribEventType, RibActionState.STARTED) ribAction() - emitRibEventAction(ribClass, ribComponentType, ribEventType, RibActionType.COMPLETED) + ribClassName?.emitRibEventAction(ribComponentType, ribEventType, RibActionState.COMPLETED) } + private fun Any.asQualifiedName(): String? = javaClass.kotlin.qualifiedName + /** * Emits emission of ATTACHED/DETACHED events for each RIB component. * - * @param ribClass Class names for custom RIB implementations (e.g. LoggedInInteractor, - * UiRibWorker, etc) * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) - * @param ribActionType: RibActionType, + * @param ribActionState: RibActionType, */ - private fun emitRibEventAction( - ribClass: KClass<*>, + private fun String.emitRibEventAction( ribComponentType: RibComponentType, ribEventType: RibEventType, - ribActionType: RibActionType, + ribActionState: RibActionState, ) { - val ribClassName = ribClass.qualifiedName - - // There's no point to emit emission if we don't know which RIB component name was triggered - ribClassName?.let { - val ribEventData = - RibActionInfo( - it, - ribComponentType, - ribEventType, - ribActionType, - ) - mutableRibDurationEvents.tryEmit(ribEventData) - } + val ribActionInfo = + RibActionInfo( + this, + ribComponentType, + ribEventType, + ribActionState, + ) + mutableRibDurationEvents.tryEmit(ribActionInfo) } } -public enum class RibActionType { - STARTED, - COMPLETED, -} - /** Holds relevant RIB event information */ public data class RibActionInfo( /** Related RIB class name */ @@ -108,8 +96,16 @@ public data class RibActionInfo( /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ val ribComponentType: RibComponentType, + + /** Represents the RIB event type, e.g. ATTACHED/DETACHED */ val ribEventType: RibEventType, - /** RIB component event type PRE_ATTACH */ - val ribActionType: RibActionType, + /** RIB Action state (e.g. event to be called before/after didBecomeActive, willLoad, etc) */ + val ribActionState: RibActionState, ) + +/** Represents status for each RibAction */ +public enum class RibActionState { + STARTED, + COMPLETED, +} 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 2130c9b20..920a5d74d 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 @@ -19,7 +19,7 @@ import android.os.Looper import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting -import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents +import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList @@ -115,13 +115,12 @@ protected constructor( } } - callRibActionAndEmitEvents( - childRouter.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { children.add(childRouter) }, + childRouter, RibComponentType.ROUTER, RibEventType.ATTACHED, - ) { - children.add(childRouter) - } + ) ribRefWatcher.logBreadcrumb( "ATTACHED", @@ -167,13 +166,12 @@ protected constructor( } } - callRibActionAndEmitEvents( - childRouter.javaClass.kotlin, + triggerRibActionAndEmitEvents( + { childRouter.dispatchDetach() }, + childRouter, RibComponentType.ROUTER, RibEventType.DETACHED, - ) { - childRouter.dispatchDetach() - } + ) if (isChildRemoved) { RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) 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 1d175b817..2e3063419 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 @@ -18,7 +18,7 @@ package com.uber.rib.core import androidx.annotation.VisibleForTesting import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.LifecycleScopeProvider -import com.uber.rib.core.RibEvents.callRibActionAndEmitEvents +import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import com.uber.rib.core.lifecycle.PresenterEvent import com.uber.rib.core.lifecycle.WorkerEvent @@ -265,35 +265,22 @@ private fun > Worker.bind( lifecycle .takeWhile { it < lifecycleRange.endInclusive } .onCompletion { - bindAndReportWorkerInfo( + triggerRibActionAndEmitEvents( + { onStop() }, + this@bind, + RibComponentType.DEPRECATED_WORKER, RibEventType.DETACHED, - ) { - onStop() - } + ) completable.onComplete() } .collect { - bindAndReportWorkerInfo( + triggerRibActionAndEmitEvents( + { onStart(workerScopeProvider) }, + this@bind, + RibComponentType.DEPRECATED_WORKER, RibEventType.ATTACHED, - ) { - onStart(workerScopeProvider) - } + ) } } return WorkerUnbinder(job::cancel) } - -private inline fun Worker.bindAndReportWorkerInfo( - ribEventType: RibEventType, - crossinline workerBinderAction: Worker.() -> Unit, -) { - val workerClass = this.javaClass.kotlin - - callRibActionAndEmitEvents( - this.javaClass.kotlin, - RibComponentType.DEPRECATED_WORKER, - ribEventType, - ) { - workerBinderAction() - } -} diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 7e5ce42b2..8d08a5792 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -62,13 +62,13 @@ class InteractorAndRouterTest { val ribActionInfoValues = ribActionInfoObserver.values() Truth.assertThat( ribActionInfoValues.contains( - buildInteractorAction(RibEventType.ATTACHED, RibActionType.STARTED), + buildInteractorAction(RibEventType.ATTACHED, RibActionState.STARTED), ), ) .isTrue() Truth.assertThat( ribActionInfoValues.contains( - buildInteractorAction(RibEventType.ATTACHED, RibActionType.COMPLETED), + buildInteractorAction(RibEventType.ATTACHED, RibActionState.COMPLETED), ), ) .isTrue() @@ -88,13 +88,13 @@ class InteractorAndRouterTest { val ribActionInfoValues = ribActionInfoObserver.values() Truth.assertThat( ribActionInfoValues.contains( - buildInteractorAction(RibEventType.DETACHED, RibActionType.STARTED), + buildInteractorAction(RibEventType.DETACHED, RibActionState.STARTED), ), ) .isTrue() Truth.assertThat( ribActionInfoValues.contains( - buildInteractorAction(RibEventType.DETACHED, RibActionType.COMPLETED), + buildInteractorAction(RibEventType.DETACHED, RibActionState.COMPLETED), ), ) .isTrue() @@ -297,13 +297,13 @@ class InteractorAndRouterTest { private fun buildInteractorAction( ribEventType: RibEventType, - ribActionType: RibActionType, + ribActionState: RibActionState, ) = RibActionInfo( "com.uber.rib.core.InteractorAndRouterTest.TestInteractor", ribComponentType = RibComponentType.INTERACTOR, ribEventType = ribEventType, - ribActionType = ribActionType, + ribActionState = ribActionState, ) companion object { diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 67dca9e0d..22c1d3616 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -222,7 +222,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { expectedClassName, RibComponentType.DEPRECATED_WORKER, ribEventType, - RibActionType.COMPLETED, + RibActionState.COMPLETED, ) companion object { From 546ac786b1574b355c15321fb333235ef219bd86 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 09:52:20 -0700 Subject: [PATCH 18/52] Adding back getInstance for RibEvents and mark it as deprecated --- .../src/main/kotlin/com/uber/rib/core/RibEvents.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 665d470a2..6a2c3f42d 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -33,6 +33,15 @@ public object RibEvents { @JvmStatic public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() + @Deprecated( + message = + """ + getInstance() is no longer needed. You can use directly access properties [routerEvents] or [ribActionEvents] + """, + replaceWith = ReplaceWith("RibEvents.routerEvents or RibEvents.ribActionEvents"), + ) + @JvmStatic public fun getInstance(): RibEvents = this + /** * @param eventType [RibEventType] * @param child [Router] From 062b00294dd88779b0a49344c0b3d7e9fa20208d Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 09:55:30 -0700 Subject: [PATCH 19/52] Removing again getInstance of RibEvents --- .../src/main/kotlin/com/uber/rib/core/RibEvents.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 6a2c3f42d..665d470a2 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -33,15 +33,6 @@ public object RibEvents { @JvmStatic public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() - @Deprecated( - message = - """ - getInstance() is no longer needed. You can use directly access properties [routerEvents] or [ribActionEvents] - """, - replaceWith = ReplaceWith("RibEvents.routerEvents or RibEvents.ribActionEvents"), - ) - @JvmStatic public fun getInstance(): RibEvents = this - /** * @param eventType [RibEventType] * @param child [Router] From 747b8318fc2340fff7253154379049d6a5a2e473 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 11:26:20 -0700 Subject: [PATCH 20/52] Change actionBlock declaration order - Use MainScope for ApplicationLevelWorkerLogger demo - Add thread name to always keep track of original caller thread name --- .../logger/ApplicationLevelWorkerLogger.kt | 14 ++++++------- .../kotlin/com/uber/rib/core/Interactor.kt | 21 ++++++++++++------- .../kotlin/com/uber/rib/core/RibEvents.kt | 6 +++++- .../main/kotlin/com/uber/rib/core/Router.kt | 10 +++++---- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 11 ++++++---- .../uber/rib/core/InteractorAndRouterTest.kt | 1 + .../com/uber/rib/core/WorkerBinderTest.kt | 1 + 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index 51e9622a5..5176fed97 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -23,8 +23,7 @@ import com.uber.rib.core.RibDispatchers import com.uber.rib.core.RibEvents import java.lang.System.currentTimeMillis import java.util.concurrent.ConcurrentHashMap -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import kotlinx.coroutines.rx2.asFlow @@ -34,17 +33,17 @@ import kotlinx.coroutines.rx2.asFlow * 2. Could report expensive workers on Ui thread and crash on Debug builds for early detection * 3. More tailored aggregation if needed. * - * IMPORTANT: Given logic at [log] will be running upon Interactor/Router/Presenter/Worker - * ATTACH/DETACH, the added logic should guaranteed that we are not impacting app performance + * IMPORTANT: Given logic at [logWorkerDuration] will be running upon + * Interactor/Router/Presenter/Worker ATTACH/DETACH, the added logic should guaranteed that we are + * not impacting app performance */ object ApplicationLevelWorkerLogger { private const val LOG_TAG = "RibEventLogger" private val concurrentHashMap: ConcurrentHashMap = ConcurrentHashMap() - @OptIn(DelicateCoroutinesApi::class) fun start() { - GlobalScope.launch(RibDispatchers.Unconfined) { + MainScope().launch(RibDispatchers.Default) { RibEvents.ribActionEvents .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } .asFlow() @@ -69,10 +68,9 @@ object ApplicationLevelWorkerLogger { private fun RibActionInfo.logDuration(preOnStartDuration: Long) { val totalDuration = currentTimeMillis() - preOnStartDuration - val currentThreadName = Thread.currentThread().name Log.d( LOG_TAG, - "WORKER_BINDING_INFO -> ${this.className} ${this.ribEventType} took $totalDuration ms on $currentThreadName thread", + "WORKER_BINDING_INFO -> ${this.className} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", ) } } 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 dd6865121..20c4a2cdb 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 @@ -107,38 +107,43 @@ public abstract class Interactor

>() : InteractorType { val presenter = (getPresenter() as? Presenter) presenter?.let { triggerRibActionAndEmitEvents( - { it.dispatchLoad() }, it, RibComponentType.PRESENTER, RibEventType.ATTACHED, - ) + ) { + it.dispatchLoad() + } } triggerRibActionAndEmitEvents( - { didBecomeActive(savedInstanceState) }, this, RibComponentType.INTERACTOR, RibEventType.ATTACHED, - ) + ) { + didBecomeActive(savedInstanceState) + } } public open fun dispatchDetach(): P { val presenter = (getPresenter() as? Presenter) presenter?.let { triggerRibActionAndEmitEvents( - { it.dispatchUnload() }, it, RibComponentType.PRESENTER, RibEventType.DETACHED, - ) + ) { + it.dispatchUnload() + } } triggerRibActionAndEmitEvents( - { willResignActive() }, this, RibComponentType.INTERACTOR, RibEventType.DETACHED, - ) + ) { + willResignActive() + } + _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) return getPresenter() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 665d470a2..cf42e5bc6 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -53,10 +53,10 @@ public object RibEvents { * @param ribEventType RIB event type (e.g. ATTACH/DETACH) */ internal fun triggerRibActionAndEmitEvents( - ribAction: () -> Unit, ribCallerClassType: T, ribComponentType: RibComponentType, ribEventType: RibEventType, + ribAction: () -> Unit, ) { val ribClassName = ribCallerClassType.asQualifiedName() ribClassName?.emitRibEventAction(ribComponentType, ribEventType, RibActionState.STARTED) @@ -84,6 +84,7 @@ public object RibEvents { ribComponentType, ribEventType, ribActionState, + Thread.currentThread().name, ) mutableRibDurationEvents.tryEmit(ribActionInfo) } @@ -102,6 +103,9 @@ public data class RibActionInfo( /** RIB Action state (e.g. event to be called before/after didBecomeActive, willLoad, etc) */ val ribActionState: RibActionState, + + /** Original caller thread where the RIB action happens */ + val originalCallerThreadName: String, ) /** Represents status for each RibAction */ 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 920a5d74d..84bf5e414 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 @@ -116,11 +116,12 @@ protected constructor( } triggerRibActionAndEmitEvents( - { children.add(childRouter) }, childRouter, RibComponentType.ROUTER, RibEventType.ATTACHED, - ) + ) { + children.add(childRouter) + } ribRefWatcher.logBreadcrumb( "ATTACHED", @@ -167,11 +168,12 @@ protected constructor( } triggerRibActionAndEmitEvents( - { childRouter.dispatchDetach() }, childRouter, RibComponentType.ROUTER, RibEventType.DETACHED, - ) + ) { + childRouter.dispatchDetach() + } if (isChildRemoved) { RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) 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 2e3063419..3da635d49 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 @@ -266,20 +266,23 @@ private fun > Worker.bind( .takeWhile { it < lifecycleRange.endInclusive } .onCompletion { triggerRibActionAndEmitEvents( - { onStop() }, this@bind, RibComponentType.DEPRECATED_WORKER, RibEventType.DETACHED, - ) + ) { + onStop() + } + completable.onComplete() } .collect { triggerRibActionAndEmitEvents( - { onStart(workerScopeProvider) }, this@bind, RibComponentType.DEPRECATED_WORKER, RibEventType.ATTACHED, - ) + ) { + onStart(workerScopeProvider) + } } } return WorkerUnbinder(job::cancel) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 8d08a5792..004af7bb7 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -304,6 +304,7 @@ class InteractorAndRouterTest { ribComponentType = RibComponentType.INTERACTOR, ribEventType = ribEventType, ribActionState = ribActionState, + Thread.currentThread().name, ) companion object { diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 22c1d3616..2cce74e9b 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -223,6 +223,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { RibComponentType.DEPRECATED_WORKER, ribEventType, RibActionState.COMPLETED, + Thread.currentThread().name, ) companion object { From 5a42204fe8718ad131e6468253f1f3e2abbba450 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 12:55:20 -0700 Subject: [PATCH 21/52] Update JUnit tests --- .../uber/rib/core/InteractorAndRouterTest.kt | 49 ++++++------------- .../com/uber/rib/core/RibEventsUtils.kt | 32 ++++++++++++ .../com/uber/rib/core/WorkerBinderTest.kt | 44 ++++++++++------- 3 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index 004af7bb7..d1a84c3b4 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -18,6 +18,7 @@ package com.uber.rib.core import com.google.common.truth.Truth import com.uber.autodispose.lifecycle.LifecycleEndedException import com.uber.rib.core.RibEvents.ribActionEvents +import com.uber.rib.core.RibEventsUtils.assertRibActionInfo import com.uber.rib.core.RibRefWatcher.Companion.getInstance import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.observers.TestObserver @@ -60,18 +61,14 @@ class InteractorAndRouterTest { // Then. val ribActionInfoValues = ribActionInfoObserver.values() - Truth.assertThat( - ribActionInfoValues.contains( - buildInteractorAction(RibEventType.ATTACHED, RibActionState.STARTED), - ), + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibComponentType.INTERACTOR, + RibActionState.COMPLETED, + "com.uber.rib.core.InteractorAndRouterTest.TestInteractor", ) - .isTrue() - Truth.assertThat( - ribActionInfoValues.contains( - buildInteractorAction(RibEventType.ATTACHED, RibActionState.COMPLETED), - ), - ) - .isTrue() verify(childInteractor).dispatchAttach(null) } @@ -86,18 +83,14 @@ class InteractorAndRouterTest { // Then. val ribActionInfoValues = ribActionInfoObserver.values() - Truth.assertThat( - ribActionInfoValues.contains( - buildInteractorAction(RibEventType.DETACHED, RibActionState.STARTED), - ), - ) - .isTrue() - Truth.assertThat( - ribActionInfoValues.contains( - buildInteractorAction(RibEventType.DETACHED, RibActionState.COMPLETED), - ), + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.DETACHED, + RibComponentType.ROUTER, + RibActionState.COMPLETED, + "com.uber.rib.core.FakeRouter", ) - .isTrue() verify(childInteractor).dispatchDetach() } @@ -295,18 +288,6 @@ class InteractorAndRouterTest { } } - private fun buildInteractorAction( - ribEventType: RibEventType, - ribActionState: RibActionState, - ) = - RibActionInfo( - "com.uber.rib.core.InteractorAndRouterTest.TestInteractor", - ribComponentType = RibComponentType.INTERACTOR, - ribEventType = ribEventType, - ribActionState = ribActionState, - Thread.currentThread().name, - ) - companion object { private const val TEST_KEY = "test_key" private const val TEST_VALUE = "test_value" diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt new file mode 100644 index 000000000..b640c492a --- /dev/null +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt @@ -0,0 +1,32 @@ +/* + * 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 + +import com.google.common.truth.Truth + +object RibEventsUtils { + internal fun RibActionInfo.assertRibActionInfo( + expectedRibEventType: RibEventType, + expectedRibComponentType: RibComponentType, + ribActionState: RibActionState, + ribClassName: String, + ) { + Truth.assertThat(this.ribEventType).isEqualTo(expectedRibEventType) + Truth.assertThat(this.ribComponentType).isEqualTo(expectedRibComponentType) + Truth.assertThat(this.ribActionState).isEqualTo(ribActionState) + Truth.assertThat(this.className).isEqualTo(ribClassName) + } +} diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 2cce74e9b..4544c80a5 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -18,6 +18,7 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat import com.jakewharton.rxrelay2.BehaviorRelay import com.uber.rib.core.RibEvents.ribActionEvents +import com.uber.rib.core.RibEventsUtils.assertRibActionInfo import com.uber.rib.core.WorkerBinder.bind import com.uber.rib.core.WorkerBinder.bindToWorkerLifecycle import com.uber.rib.core.WorkerBinder.mapInteractorLifecycleToWorker @@ -173,16 +174,21 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Test fun bind_withUnconfinedCoroutineDispatchers_shouldReportBinderInformationForOnStart() = runTest { - val expectedRibActionInfo = buildExpectedWorkerRibAction() ribActionEvents.subscribe(ribActionInfoObserver) bindFakeWorker() val ribActionInfoValues = ribActionInfoObserver.values() - assertThat(ribActionInfoValues.last()).isEqualTo(expectedRibActionInfo) + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibComponentType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.FakeWorker", + ) } @Test fun bind_multipleWorkers_shouldReportBinderUiWorker() = runTest { - val expectedUiRibActionEvent = buildExpectedWorkerRibAction("com.uber.rib.core.UiWorker") ribActionEvents.subscribe(ribActionInfoObserver) val uiWorker = UiWorker() prepareInteractor() @@ -190,18 +196,30 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { bind(interactor, workers) advanceUntilIdle() val ribActionInfoValues = ribActionInfoObserver.values() - assertThat(ribActionInfoValues.last()).isEqualTo(expectedUiRibActionEvent) + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibComponentType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.UiWorker", + ) } @Test fun unbind_withUnconfinedCoroutineDispatchers_shouldReportBinderDurationForOnStop() = runTest { - val expectedUiRibActionEvent = - buildExpectedWorkerRibAction(ribEventType = RibEventType.DETACHED) ribActionEvents.subscribe(ribActionInfoObserver) val unbinder = bindFakeWorker() unbinder.unbind() val ribActionInfoValues = ribActionInfoObserver.values() - assertThat(ribActionInfoValues.last()).isEqualTo(expectedUiRibActionEvent) + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.DETACHED, + RibComponentType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.FakeWorker", + ) } private fun bindFakeWorker(): WorkerUnbinder { @@ -214,18 +232,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { interactor.enableTestScopeOverride() } - private fun buildExpectedWorkerRibAction( - expectedClassName: String = "com.uber.rib.core.FakeWorker", - ribEventType: RibEventType = RibEventType.ATTACHED, - ): RibActionInfo = - RibActionInfo( - expectedClassName, - RibComponentType.DEPRECATED_WORKER, - ribEventType, - RibActionState.COMPLETED, - Thread.currentThread().name, - ) - companion object { @JvmStatic @Parameterized.Parameters(name = "adaptFromRibCoroutineWorker = {0}") From 7009b817d6b37c14f683d787aff58bfbc71c475f Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 15:35:50 -0700 Subject: [PATCH 22/52] Apply PR feedback --- .../logger/ApplicationLevelWorkerLogger.kt | 36 ++++++++++--------- .../kotlin/com/uber/rib/core/RibEvents.kt | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index 5176fed97..a696c7aa4 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -19,11 +19,11 @@ import android.util.Log import com.uber.rib.core.RibActionInfo import com.uber.rib.core.RibActionState import com.uber.rib.core.RibComponentType -import com.uber.rib.core.RibDispatchers import com.uber.rib.core.RibEvents import java.lang.System.currentTimeMillis import java.util.concurrent.ConcurrentHashMap -import kotlinx.coroutines.MainScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.rx2.asFlow @@ -38,12 +38,13 @@ import kotlinx.coroutines.rx2.asFlow * not impacting app performance */ object ApplicationLevelWorkerLogger { - private const val LOG_TAG = "RibEventLogger" + private const val LOG_TAG = "WorkerLogger" - private val concurrentHashMap: ConcurrentHashMap = ConcurrentHashMap() + private val workerTimeStampMap = ConcurrentHashMap() + @OptIn(DelicateCoroutinesApi::class) fun start() { - MainScope().launch(RibDispatchers.Default) { + GlobalScope.launch { RibEvents.ribActionEvents .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } .asFlow() @@ -54,23 +55,26 @@ object ApplicationLevelWorkerLogger { private fun RibActionInfo.logWorkerDuration() { val ribComponentKey = this.className if (ribActionState == RibActionState.STARTED && !ribComponentKey.isClassNameInMap()) { - concurrentHashMap[ribComponentKey] = currentTimeMillis() - } - - if (ribActionState == RibActionState.COMPLETED && ribComponentKey.isClassNameInMap()) { - val preOnStartDuration = concurrentHashMap[ribComponentKey] - preOnStartDuration?.let { this.logDuration(it) } - concurrentHashMap.remove(ribComponentKey) + workerTimeStampMap[ribComponentKey] = currentTimeMillis() + } else if (ribActionState == RibActionState.COMPLETED && ribComponentKey.isClassNameInMap()) { + val startedTimeStamp = workerTimeStampMap[ribComponentKey] + startedTimeStamp?.let { + val totalDuration = getTotalDuration(it) + this.logDuration(totalDuration) + } + workerTimeStampMap.remove(ribComponentKey) } } - private fun String.isClassNameInMap(): Boolean = concurrentHashMap.containsKey(this) + private fun String.isClassNameInMap(): Boolean = workerTimeStampMap.containsKey(this) + + private fun getTotalDuration(preOnStartDuration: Long): Long = + currentTimeMillis() - preOnStartDuration - private fun RibActionInfo.logDuration(preOnStartDuration: Long) { - val totalDuration = currentTimeMillis() - preOnStartDuration + private fun RibActionInfo.logDuration(totalDuration: Long) { Log.d( LOG_TAG, - "WORKER_BINDING_INFO -> ${this.className} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", + "${this.className} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", ) } } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index cf42e5bc6..cd08fc7a3 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -52,7 +52,7 @@ public object RibEvents { * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) */ - internal fun triggerRibActionAndEmitEvents( + internal inline fun triggerRibActionAndEmitEvents( ribCallerClassType: T, ribComponentType: RibComponentType, ribEventType: RibEventType, From 2edee91e1d7a109bb1684a0ed623e85f450a3baa Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 22:08:06 -0700 Subject: [PATCH 23/52] First proposal after PR feedback --- .../logger/ApplicationLevelWorkerLogger.kt | 2 + .../kotlin/com/uber/rib/core/Interactor.kt | 2 +- .../kotlin/com/uber/rib/core/Presenter.kt | 2 +- .../com/uber/rib/core/RibComponentType.kt | 4 +- .../com/uber/rib/core/RibCoroutineWorker.kt | 3 +- .../kotlin/com/uber/rib/core/RibEvents.kt | 41 ++++++++++++++----- .../main/kotlin/com/uber/rib/core/Router.kt | 2 +- .../main/kotlin/com/uber/rib/core/Worker.kt | 2 +- .../uber/rib/core/InteractorAndRouterTest.kt | 3 +- .../com/uber/rib/core/WorkerBinderTest.kt | 6 +++ 10 files changed, 49 insertions(+), 18 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index a696c7aa4..d6ca61e35 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -44,6 +44,8 @@ object ApplicationLevelWorkerLogger { @OptIn(DelicateCoroutinesApi::class) fun start() { + RibEvents.startCapturingRibActionInfo() + GlobalScope.launch { RibEvents.ribActionEvents .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } 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 20c4a2cdb..946f965b6 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 @@ -37,7 +37,7 @@ import kotlinx.coroutines.rx2.asObservable * @param

the type of [Presenter]. * @param the type of [Router]. */ -public abstract class Interactor

>() : InteractorType { +public abstract class Interactor

>() : InteractorType, RibComponent { @Inject public lateinit var injectedPresenter: P internal var actualPresenter: P? = null private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) 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 4d5c03265..b9871c979 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 @@ -33,7 +33,7 @@ import org.checkerframework.checker.guieffect.qual.UIEffect * practice this caused confusion: if both a presenter and interactor can perform complex rx logic * it becomes unclear where you should write your bussiness logic. */ -public abstract class Presenter : ScopeProvider { +public abstract class Presenter : ScopeProvider, RibComponent { private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) public open val lifecycleFlow: SharedFlow get() = _lifecycleFlow diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt index 8f9c6308b..16b67b32e 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt @@ -15,11 +15,11 @@ */ package com.uber.rib.core +public interface RibComponent + public enum class RibComponentType { ROUTER, PRESENTER, INTERACTOR, DEPRECATED_WORKER, - - /** RIB_COROUTINE_WORKER -> To be added on next releases */ } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index 9ba03ddfb..ab14e965e 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -43,7 +43,8 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext /** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */ -public fun interface RibCoroutineWorker { +public fun interface RibCoroutineWorker : RibComponent { + /** Called when worker is started. Children coroutines can be launched in [scope]. */ public suspend fun onStart(scope: CoroutineScope) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index cd08fc7a3..639d509e4 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -33,12 +33,24 @@ public object RibEvents { @JvmStatic public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() + private var allowRibActionEmissions = false + + /** + * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest + * application point) + */ + @JvmStatic + public fun startCapturingRibActionInfo() { + this.allowRibActionEmissions = true + } + /** * @param eventType [RibEventType] * @param child [Router] * @param parent [Router] and null for the root ribs that are directly attached to * RibActivity/Fragment */ + @JvmStatic public fun emitRouterEvent(eventType: RibEventType, child: Router<*>, parent: Router<*>?) { mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent)) } @@ -48,24 +60,26 @@ public object RibEvents { * for each RIB component. * * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc - * @param ribCallerClassType Related RIB component class type + * @param ribComponent Related RIB component * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) */ - internal inline fun triggerRibActionAndEmitEvents( - ribCallerClassType: T, + internal inline fun triggerRibActionAndEmitEvents( + ribComponent: RibComponent, ribComponentType: RibComponentType, ribEventType: RibEventType, ribAction: () -> Unit, ) { - val ribClassName = ribCallerClassType.asQualifiedName() - ribClassName?.emitRibEventAction(ribComponentType, ribEventType, RibActionState.STARTED) + emitRibEventActionIfNeeded(ribComponent, ribComponentType, ribEventType, RibActionState.STARTED) ribAction() - ribClassName?.emitRibEventAction(ribComponentType, ribEventType, RibActionState.COMPLETED) + emitRibEventActionIfNeeded( + ribComponent, + ribComponentType, + ribEventType, + RibActionState.COMPLETED, + ) } - private fun Any.asQualifiedName(): String? = javaClass.kotlin.qualifiedName - /** * Emits emission of ATTACHED/DETACHED events for each RIB component. * @@ -73,14 +87,21 @@ public object RibEvents { * @param ribEventType RIB event type (e.g. ATTACH/DETACH) * @param ribActionState: RibActionType, */ - private fun String.emitRibEventAction( + private fun emitRibEventActionIfNeeded( + ribComponent: RibComponent, ribComponentType: RibComponentType, ribEventType: RibEventType, ribActionState: RibActionState, ) { + if (!allowRibActionEmissions) { + // Unless specified explicitly via [RibEvents.startCapturingRibActionInfo] there is no need + // to create unnecessary objects if no one is observing/collecting RibAction events + return + } + val ribActionInfo = RibActionInfo( - this, + ribComponent.javaClass.name, ribComponentType, ribEventType, ribActionState, 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 84bf5e414..3ba5e082f 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 @@ -34,7 +34,7 @@ protected constructor( public open val interactor: I, private val ribRefWatcher: RibRefWatcher, private val mainThread: Thread, -) { +) : RibComponent { private val children: MutableList> = CopyOnWriteArrayList() private val interactorGeneric: Interactor<*, *> get() = interactor as Interactor<*, *> diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index b6f378c72..1c5dc3dad 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.CoroutineDispatcher """, replaceWith = ReplaceWith("RibCoroutineWorker"), ) -public interface Worker { +public interface Worker : RibComponent { /** * When overriden, will specify on which [CoroutineContext] the [Worker] will be bound via diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index d1a84c3b4..d2bf29771 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -49,6 +49,7 @@ class InteractorAndRouterTest { } interactor = TestInteractor(childInteractor) router = TestRouter(interactor, component) + RibEvents.startCapturingRibActionInfo() } @Test @@ -67,7 +68,7 @@ class InteractorAndRouterTest { RibEventType.ATTACHED, RibComponentType.INTERACTOR, RibActionState.COMPLETED, - "com.uber.rib.core.InteractorAndRouterTest.TestInteractor", + "com.uber.rib.core.InteractorAndRouterTest${'$'}TestInteractor", ) verify(childInteractor).dispatchAttach(null) } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 4544c80a5..6c4b64020 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -59,6 +60,11 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { private val interactor = FakeInteractor>() private val ribActionInfoObserver = TestObserver() + @Before + fun setUp() { + RibEvents.startCapturingRibActionInfo() + } + @Test fun bind_whenInteractorAttached_shouldStartWorker() { val lifecycle = BehaviorRelay.createDefault(InteractorEvent.ACTIVE) From 7f251b1a6cabc0439e03ba8a4e5adf85de826e09 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Wed, 21 Jun 2023 22:18:16 -0700 Subject: [PATCH 24/52] Rename startCapturingRibActionInfo --- .../root/logger/ApplicationLevelWorkerLogger.kt | 2 +- .../src/main/kotlin/com/uber/rib/core/RibEvents.kt | 10 +++++----- .../com/uber/rib/core/InteractorAndRouterTest.kt | 2 +- .../test/kotlin/com/uber/rib/core/WorkerBinderTest.kt | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index d6ca61e35..9392f5802 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -44,7 +44,7 @@ object ApplicationLevelWorkerLogger { @OptIn(DelicateCoroutinesApi::class) fun start() { - RibEvents.startCapturingRibActionInfo() + RibEvents.allowRibActionEmissions() GlobalScope.launch { RibEvents.ribActionEvents diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 639d509e4..f60553636 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -33,15 +33,15 @@ public object RibEvents { @JvmStatic public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() - private var allowRibActionEmissions = false + private var areRibActionEmissionsAllowed = false /** * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest * application point) */ @JvmStatic - public fun startCapturingRibActionInfo() { - this.allowRibActionEmissions = true + public fun allowRibActionEmissions() { + this.areRibActionEmissionsAllowed = true } /** @@ -93,8 +93,8 @@ public object RibEvents { ribEventType: RibEventType, ribActionState: RibActionState, ) { - if (!allowRibActionEmissions) { - // Unless specified explicitly via [RibEvents.startCapturingRibActionInfo] there is no need + if (!areRibActionEmissionsAllowed) { + // Unless specified explicitly via [RibEvents.allowRibActionEmissions] there is no need // to create unnecessary objects if no one is observing/collecting RibAction events return } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index d2bf29771..b5fd42663 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -49,7 +49,7 @@ class InteractorAndRouterTest { } interactor = TestInteractor(childInteractor) router = TestRouter(interactor, component) - RibEvents.startCapturingRibActionInfo() + RibEvents.allowRibActionEmissions() } @Test diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 6c4b64020..2b9b18410 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -62,7 +62,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { - RibEvents.startCapturingRibActionInfo() + RibEvents.allowRibActionEmissions() } @Test From 82019845eab41e19861071e80008f6e9872bac26 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 10:24:00 -0700 Subject: [PATCH 25/52] PR feedback: Rename RibComponent and add switch method for RibEvents --- .../logger/ApplicationLevelWorkerLogger.kt | 21 +++---- .../kotlin/com/uber/rib/core/Interactor.kt | 10 ++-- .../kotlin/com/uber/rib/core/Presenter.kt | 2 +- .../com/uber/rib/core/RibComponentType.kt | 25 --------- .../com/uber/rib/core/RibCoroutineWorker.kt | 2 +- .../kotlin/com/uber/rib/core/RibEvents.kt | 55 ++++++++++++++----- .../main/kotlin/com/uber/rib/core/Router.kt | 6 +- .../main/kotlin/com/uber/rib/core/Worker.kt | 2 +- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 4 +- .../uber/rib/core/InteractorAndRouterTest.kt | 20 ++++++- .../com/uber/rib/core/RibEventsUtils.kt | 6 +- .../com/uber/rib/core/WorkerBinderTest.kt | 16 ++++-- 12 files changed, 96 insertions(+), 73 deletions(-) delete mode 100644 android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index 9392f5802..887a41079 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -18,7 +18,7 @@ package com.uber.rib.workers.root.logger import android.util.Log import com.uber.rib.core.RibActionInfo import com.uber.rib.core.RibActionState -import com.uber.rib.core.RibComponentType +import com.uber.rib.core.RibEventEmitterType import com.uber.rib.core.RibEvents import java.lang.System.currentTimeMillis import java.util.concurrent.ConcurrentHashMap @@ -44,27 +44,28 @@ object ApplicationLevelWorkerLogger { @OptIn(DelicateCoroutinesApi::class) fun start() { - RibEvents.allowRibActionEmissions() + RibEvents.enableRibActionEmissions() GlobalScope.launch { RibEvents.ribActionEvents - .filter { it.ribComponentType == RibComponentType.DEPRECATED_WORKER } + .filter { it.RibEventEmitterType == RibEventEmitterType.DEPRECATED_WORKER } .asFlow() .collect { it.logWorkerDuration() } } } private fun RibActionInfo.logWorkerDuration() { - val ribComponentKey = this.className - if (ribActionState == RibActionState.STARTED && !ribComponentKey.isClassNameInMap()) { - workerTimeStampMap[ribComponentKey] = currentTimeMillis() - } else if (ribActionState == RibActionState.COMPLETED && ribComponentKey.isClassNameInMap()) { - val startedTimeStamp = workerTimeStampMap[ribComponentKey] + if (ribActionState == RibActionState.STARTED && !ribEventEmitterName.isClassNameInMap()) { + workerTimeStampMap[this.ribEventEmitterName] = currentTimeMillis() + } else if ( + ribActionState == RibActionState.COMPLETED && ribEventEmitterName.isClassNameInMap() + ) { + val startedTimeStamp = workerTimeStampMap[ribEventEmitterName] startedTimeStamp?.let { val totalDuration = getTotalDuration(it) this.logDuration(totalDuration) } - workerTimeStampMap.remove(ribComponentKey) + workerTimeStampMap.remove(ribEventEmitterName) } } @@ -76,7 +77,7 @@ object ApplicationLevelWorkerLogger { private fun RibActionInfo.logDuration(totalDuration: Long) { Log.d( LOG_TAG, - "${this.className} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", + "${this.ribEventEmitterName} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", ) } } 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 946f965b6..1f359bb05 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 @@ -37,7 +37,7 @@ import kotlinx.coroutines.rx2.asObservable * @param

the type of [Presenter]. * @param the type of [Router]. */ -public abstract class Interactor

>() : InteractorType, RibComponent { +public abstract class Interactor

>() : InteractorType, RibEventEmitter { @Inject public lateinit var injectedPresenter: P internal var actualPresenter: P? = null private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) @@ -108,7 +108,7 @@ public abstract class Interactor

>() : InteractorType, Rib presenter?.let { triggerRibActionAndEmitEvents( it, - RibComponentType.PRESENTER, + RibEventEmitterType.PRESENTER, RibEventType.ATTACHED, ) { it.dispatchLoad() @@ -117,7 +117,7 @@ public abstract class Interactor

>() : InteractorType, Rib triggerRibActionAndEmitEvents( this, - RibComponentType.INTERACTOR, + RibEventEmitterType.INTERACTOR, RibEventType.ATTACHED, ) { didBecomeActive(savedInstanceState) @@ -129,7 +129,7 @@ public abstract class Interactor

>() : InteractorType, Rib presenter?.let { triggerRibActionAndEmitEvents( it, - RibComponentType.PRESENTER, + RibEventEmitterType.PRESENTER, RibEventType.DETACHED, ) { it.dispatchUnload() @@ -138,7 +138,7 @@ public abstract class Interactor

>() : InteractorType, Rib triggerRibActionAndEmitEvents( this, - RibComponentType.INTERACTOR, + RibEventEmitterType.INTERACTOR, RibEventType.DETACHED, ) { willResignActive() 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 b9871c979..fd1fbf4a3 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 @@ -33,7 +33,7 @@ import org.checkerframework.checker.guieffect.qual.UIEffect * practice this caused confusion: if both a presenter and interactor can perform complex rx logic * it becomes unclear where you should write your bussiness logic. */ -public abstract class Presenter : ScopeProvider, RibComponent { +public abstract class Presenter : ScopeProvider, RibEventEmitter { private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) public open val lifecycleFlow: SharedFlow get() = _lifecycleFlow diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt deleted file mode 100644 index 16b67b32e..000000000 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibComponentType.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 - -public interface RibComponent - -public enum class RibComponentType { - ROUTER, - PRESENTER, - INTERACTOR, - DEPRECATED_WORKER, -} diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index ab14e965e..700b12866 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext /** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */ -public fun interface RibCoroutineWorker : RibComponent { +public fun interface RibCoroutineWorker : RibEventEmitter { /** Called when worker is started. Children coroutines can be launched in [scope]. */ public suspend fun onStart(scope: CoroutineScope) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index f60553636..2b6009fd1 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -15,6 +15,7 @@ */ package com.uber.rib.core +import androidx.annotation.VisibleForTesting import io.reactivex.Observable import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -40,10 +41,16 @@ public object RibEvents { * application point) */ @JvmStatic - public fun allowRibActionEmissions() { + public fun enableRibActionEmissions() { this.areRibActionEmissionsAllowed = true } + /** Only to be used within test * */ + @VisibleForTesting + internal fun disableRibActionEmissions() { + this.areRibActionEmissionsAllowed = false + } + /** * @param eventType [RibEventType] * @param child [Router] @@ -60,21 +67,26 @@ public object RibEvents { * for each RIB component. * * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc - * @param ribComponent Related RIB component - * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter, Worker) + * @param RibEventEmitter Related RIB component + * @param RibEventEmitterType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) */ internal inline fun triggerRibActionAndEmitEvents( - ribComponent: RibComponent, - ribComponentType: RibComponentType, + RibEventEmitter: RibEventEmitter, + RibEventEmitterType: RibEventEmitterType, ribEventType: RibEventType, ribAction: () -> Unit, ) { - emitRibEventActionIfNeeded(ribComponent, ribComponentType, ribEventType, RibActionState.STARTED) + emitRibEventActionIfNeeded( + RibEventEmitter, + RibEventEmitterType, + ribEventType, + RibActionState.STARTED, + ) ribAction() emitRibEventActionIfNeeded( - ribComponent, - ribComponentType, + RibEventEmitter, + RibEventEmitterType, ribEventType, RibActionState.COMPLETED, ) @@ -83,13 +95,13 @@ public object RibEvents { /** * Emits emission of ATTACHED/DETACHED events for each RIB component. * - * @param ribComponentType The RIB component type (e.g. Interactor, Router, Presenter) + * @param RibEventEmitterType The RIB component type (e.g. Interactor, Router, Presenter) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) * @param ribActionState: RibActionType, */ private fun emitRibEventActionIfNeeded( - ribComponent: RibComponent, - ribComponentType: RibComponentType, + RibEventEmitter: RibEventEmitter, + RibEventEmitterType: RibEventEmitterType, ribEventType: RibEventType, ribActionState: RibActionState, ) { @@ -101,8 +113,8 @@ public object RibEvents { val ribActionInfo = RibActionInfo( - ribComponent.javaClass.name, - ribComponentType, + RibEventEmitter.javaClass.name, + RibEventEmitterType, ribEventType, ribActionState, Thread.currentThread().name, @@ -114,10 +126,10 @@ public object RibEvents { /** Holds relevant RIB event information */ public data class RibActionInfo( /** Related RIB class name */ - val className: String, + val ribEventEmitterName: String, /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ - val ribComponentType: RibComponentType, + val RibEventEmitterType: RibEventEmitterType, /** Represents the RIB event type, e.g. ATTACHED/DETACHED */ val ribEventType: RibEventType, @@ -129,6 +141,19 @@ public data class RibActionInfo( val originalCallerThreadName: String, ) +/** + * Contract for all related Rib Components e.g. Interactor, Presenter, Router, Workers where will be + * emitting via [ribActionEvents] + */ +public interface RibEventEmitter + +public enum class RibEventEmitterType { + ROUTER, + PRESENTER, + INTERACTOR, + DEPRECATED_WORKER, +} + /** Represents status for each RibAction */ public enum class RibActionState { STARTED, 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 3ba5e082f..5c7bc4bd6 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 @@ -34,7 +34,7 @@ protected constructor( public open val interactor: I, private val ribRefWatcher: RibRefWatcher, private val mainThread: Thread, -) : RibComponent { +) : RibEventEmitter { private val children: MutableList> = CopyOnWriteArrayList() private val interactorGeneric: Interactor<*, *> get() = interactor as Interactor<*, *> @@ -117,7 +117,7 @@ protected constructor( triggerRibActionAndEmitEvents( childRouter, - RibComponentType.ROUTER, + RibEventEmitterType.ROUTER, RibEventType.ATTACHED, ) { children.add(childRouter) @@ -169,7 +169,7 @@ protected constructor( triggerRibActionAndEmitEvents( childRouter, - RibComponentType.ROUTER, + RibEventEmitterType.ROUTER, RibEventType.DETACHED, ) { childRouter.dispatchDetach() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index 1c5dc3dad..81942f470 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.CoroutineDispatcher """, replaceWith = ReplaceWith("RibCoroutineWorker"), ) -public interface Worker : RibComponent { +public interface Worker : RibEventEmitter { /** * When overriden, will specify on which [CoroutineContext] the [Worker] will be bound via 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 3da635d49..77a7a46a9 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 @@ -267,7 +267,7 @@ private fun > Worker.bind( .onCompletion { triggerRibActionAndEmitEvents( this@bind, - RibComponentType.DEPRECATED_WORKER, + RibEventEmitterType.DEPRECATED_WORKER, RibEventType.DETACHED, ) { onStop() @@ -278,7 +278,7 @@ private fun > Worker.bind( .collect { triggerRibActionAndEmitEvents( this@bind, - RibComponentType.DEPRECATED_WORKER, + RibEventEmitterType.DEPRECATED_WORKER, RibEventType.ATTACHED, ) { onStart(workerScopeProvider) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index b5fd42663..d40126c64 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -16,6 +16,7 @@ package com.uber.rib.core import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import com.uber.autodispose.lifecycle.LifecycleEndedException import com.uber.rib.core.RibEvents.ribActionEvents import com.uber.rib.core.RibEventsUtils.assertRibActionInfo @@ -49,7 +50,7 @@ class InteractorAndRouterTest { } interactor = TestInteractor(childInteractor) router = TestRouter(interactor, component) - RibEvents.allowRibActionEmissions() + RibEvents.enableRibActionEmissions() } @Test @@ -66,13 +67,26 @@ class InteractorAndRouterTest { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibComponentType.INTERACTOR, + RibEventEmitterType.INTERACTOR, RibActionState.COMPLETED, "com.uber.rib.core.InteractorAndRouterTest${'$'}TestInteractor", ) verify(childInteractor).dispatchAttach(null) } + @Test + fun attach_withoutAllowingEmissions_shouldNotEmtRibActionEvents() { + // Given. + RibEvents.disableRibActionEmissions() + ribActionEvents.subscribe(ribActionInfoObserver) + + // When. + router.dispatchAttach(null) + + // Then. + assertThat(ribActionInfoObserver.values()).isEmpty() + } + @Test fun detach_shouldDetachChildController() { // Given. @@ -88,7 +102,7 @@ class InteractorAndRouterTest { .last() .assertRibActionInfo( RibEventType.DETACHED, - RibComponentType.ROUTER, + RibEventEmitterType.ROUTER, RibActionState.COMPLETED, "com.uber.rib.core.FakeRouter", ) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt index b640c492a..acfede728 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt @@ -20,13 +20,13 @@ import com.google.common.truth.Truth object RibEventsUtils { internal fun RibActionInfo.assertRibActionInfo( expectedRibEventType: RibEventType, - expectedRibComponentType: RibComponentType, + expectedRibEventEmitterType: RibEventEmitterType, ribActionState: RibActionState, ribClassName: String, ) { Truth.assertThat(this.ribEventType).isEqualTo(expectedRibEventType) - Truth.assertThat(this.ribComponentType).isEqualTo(expectedRibComponentType) + Truth.assertThat(this.RibEventEmitterType).isEqualTo(expectedRibEventEmitterType) Truth.assertThat(this.ribActionState).isEqualTo(ribActionState) - Truth.assertThat(this.className).isEqualTo(ribClassName) + Truth.assertThat(this.ribEventEmitterName).isEqualTo(ribClassName) } } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 2b9b18410..d1576bc36 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -62,7 +62,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { - RibEvents.allowRibActionEmissions() + RibEvents.enableRibActionEmissions() } @Test @@ -187,12 +187,20 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibComponentType.DEPRECATED_WORKER, + RibEventEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.FakeWorker", ) } + @Test + fun bind_withDisabledRibActionEvents_shouldNotEmitActionEvents() = runTest { + RibEvents.disableRibActionEmissions() + ribActionEvents.subscribe(ribActionInfoObserver) + bindFakeWorker() + assertThat(ribActionInfoObserver.values()).isEmpty() + } + @Test fun bind_multipleWorkers_shouldReportBinderUiWorker() = runTest { ribActionEvents.subscribe(ribActionInfoObserver) @@ -206,7 +214,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibComponentType.DEPRECATED_WORKER, + RibEventEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.UiWorker", ) @@ -222,7 +230,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.DETACHED, - RibComponentType.DEPRECATED_WORKER, + RibEventEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.FakeWorker", ) From 2a5db8e9d740a930fc2cb6ebe1dcc84949c93815 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 11:54:41 -0700 Subject: [PATCH 26/52] Remove unnecessary comments for test method --- .../rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 2b6009fd1..c4bd71574 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -45,7 +45,6 @@ public object RibEvents { this.areRibActionEmissionsAllowed = true } - /** Only to be used within test * */ @VisibleForTesting internal fun disableRibActionEmissions() { this.areRibActionEmissionsAllowed = false From 3e7983ac5b0a96739db47c23b064e504184707e6 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 12:10:15 -0700 Subject: [PATCH 27/52] Rename RibEventEmitter to RibActionEmitter --- .../logger/ApplicationLevelWorkerLogger.kt | 16 ++--- .../kotlin/com/uber/rib/core/Interactor.kt | 10 +-- .../kotlin/com/uber/rib/core/Presenter.kt | 2 +- .../com/uber/rib/core/RibCoroutineWorker.kt | 2 +- .../kotlin/com/uber/rib/core/RibEvents.kt | 64 +++++++++++-------- .../main/kotlin/com/uber/rib/core/Router.kt | 6 +- .../main/kotlin/com/uber/rib/core/Worker.kt | 2 +- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 4 +- .../uber/rib/core/InteractorAndRouterTest.kt | 4 +- .../com/uber/rib/core/RibEventsUtils.kt | 6 +- .../com/uber/rib/core/WorkerBinderTest.kt | 6 +- 11 files changed, 67 insertions(+), 55 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt index 887a41079..ba2d49f76 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.kt @@ -16,9 +16,9 @@ package com.uber.rib.workers.root.logger import android.util.Log +import com.uber.rib.core.RibActionEmitterType import com.uber.rib.core.RibActionInfo import com.uber.rib.core.RibActionState -import com.uber.rib.core.RibEventEmitterType import com.uber.rib.core.RibEvents import java.lang.System.currentTimeMillis import java.util.concurrent.ConcurrentHashMap @@ -48,24 +48,24 @@ object ApplicationLevelWorkerLogger { GlobalScope.launch { RibEvents.ribActionEvents - .filter { it.RibEventEmitterType == RibEventEmitterType.DEPRECATED_WORKER } + .filter { it.ribActionEmitterType == RibActionEmitterType.DEPRECATED_WORKER } .asFlow() .collect { it.logWorkerDuration() } } } private fun RibActionInfo.logWorkerDuration() { - if (ribActionState == RibActionState.STARTED && !ribEventEmitterName.isClassNameInMap()) { - workerTimeStampMap[this.ribEventEmitterName] = currentTimeMillis() + if (ribActionState == RibActionState.STARTED && !ribActionEmitterName.isClassNameInMap()) { + workerTimeStampMap[this.ribActionEmitterName] = currentTimeMillis() } else if ( - ribActionState == RibActionState.COMPLETED && ribEventEmitterName.isClassNameInMap() + ribActionState == RibActionState.COMPLETED && ribActionEmitterName.isClassNameInMap() ) { - val startedTimeStamp = workerTimeStampMap[ribEventEmitterName] + val startedTimeStamp = workerTimeStampMap[ribActionEmitterName] startedTimeStamp?.let { val totalDuration = getTotalDuration(it) this.logDuration(totalDuration) } - workerTimeStampMap.remove(ribEventEmitterName) + workerTimeStampMap.remove(ribActionEmitterName) } } @@ -77,7 +77,7 @@ object ApplicationLevelWorkerLogger { private fun RibActionInfo.logDuration(totalDuration: Long) { Log.d( LOG_TAG, - "${this.ribEventEmitterName} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", + "${this.ribActionEmitterName} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", ) } } 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 1f359bb05..a705fbd2a 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 @@ -37,7 +37,7 @@ import kotlinx.coroutines.rx2.asObservable * @param

the type of [Presenter]. * @param the type of [Router]. */ -public abstract class Interactor

>() : InteractorType, RibEventEmitter { +public abstract class Interactor

>() : InteractorType, RibActionEmitter { @Inject public lateinit var injectedPresenter: P internal var actualPresenter: P? = null private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) @@ -108,7 +108,7 @@ public abstract class Interactor

>() : InteractorType, Rib presenter?.let { triggerRibActionAndEmitEvents( it, - RibEventEmitterType.PRESENTER, + RibActionEmitterType.PRESENTER, RibEventType.ATTACHED, ) { it.dispatchLoad() @@ -117,7 +117,7 @@ public abstract class Interactor

>() : InteractorType, Rib triggerRibActionAndEmitEvents( this, - RibEventEmitterType.INTERACTOR, + RibActionEmitterType.INTERACTOR, RibEventType.ATTACHED, ) { didBecomeActive(savedInstanceState) @@ -129,7 +129,7 @@ public abstract class Interactor

>() : InteractorType, Rib presenter?.let { triggerRibActionAndEmitEvents( it, - RibEventEmitterType.PRESENTER, + RibActionEmitterType.PRESENTER, RibEventType.DETACHED, ) { it.dispatchUnload() @@ -138,7 +138,7 @@ public abstract class Interactor

>() : InteractorType, Rib triggerRibActionAndEmitEvents( this, - RibEventEmitterType.INTERACTOR, + RibActionEmitterType.INTERACTOR, RibEventType.DETACHED, ) { willResignActive() 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 fd1fbf4a3..d1f74821f 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 @@ -33,7 +33,7 @@ import org.checkerframework.checker.guieffect.qual.UIEffect * practice this caused confusion: if both a presenter and interactor can perform complex rx logic * it becomes unclear where you should write your bussiness logic. */ -public abstract class Presenter : ScopeProvider, RibEventEmitter { +public abstract class Presenter : ScopeProvider, RibActionEmitter { private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) public open val lifecycleFlow: SharedFlow get() = _lifecycleFlow diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index 700b12866..8fa735925 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -43,7 +43,7 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext /** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */ -public fun interface RibCoroutineWorker : RibEventEmitter { +public fun interface RibCoroutineWorker : RibActionEmitter { /** Called when worker is started. Children coroutines can be launched in [scope]. */ public suspend fun onStart(scope: CoroutineScope) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index c4bd71574..06cceb534 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -63,44 +63,46 @@ public object RibEvents { /** * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events - * for each RIB component. + * for each [RibActionEmitter] (e.g. Concrete Interactor, Presenter, Router, Worker). * - * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc - * @param RibEventEmitter Related RIB component - * @param RibEventEmitterType The RIB component type (e.g. Interactor, Router, Presenter, Worker) + * @param ribActionEmitter The related Rib Action emitter class + * @param ribActionEmitterType The RIB component type (e.g. Interactor, Router, Presenter, Worker) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) + * @param ribAction The related RIB action type. e.g. didBecomeActive, willLoad, etc */ internal inline fun triggerRibActionAndEmitEvents( - RibEventEmitter: RibEventEmitter, - RibEventEmitterType: RibEventEmitterType, + ribActionEmitter: RibActionEmitter, + ribActionEmitterType: RibActionEmitterType, ribEventType: RibEventType, ribAction: () -> Unit, ) { emitRibEventActionIfNeeded( - RibEventEmitter, - RibEventEmitterType, + ribActionEmitter, + ribActionEmitterType, ribEventType, RibActionState.STARTED, ) ribAction() emitRibEventActionIfNeeded( - RibEventEmitter, - RibEventEmitterType, + ribActionEmitter, + ribActionEmitterType, ribEventType, RibActionState.COMPLETED, ) } /** - * Emits emission of ATTACHED/DETACHED events for each RIB component. + * Emits ATTACHED/DETACHED events for each RIB component. * - * @param RibEventEmitterType The RIB component type (e.g. Interactor, Router, Presenter) + * @param ribActionEmitter The related Rib Action emitter class + * @param ribActionEmitterType The RIB component type (e.g. Interactor, Router, Presenter) * @param ribEventType RIB event type (e.g. ATTACH/DETACH) - * @param ribActionState: RibActionType, + * @param ribActionState: RIB action state (STARTED/COMPLETED). For example prior and after call + * of Interactor.didBecomeActive */ private fun emitRibEventActionIfNeeded( - RibEventEmitter: RibEventEmitter, - RibEventEmitterType: RibEventEmitterType, + ribActionEmitter: RibActionEmitter, + ribActionEmitterType: RibActionEmitterType, ribEventType: RibEventType, ribActionState: RibActionState, ) { @@ -112,8 +114,8 @@ public object RibEvents { val ribActionInfo = RibActionInfo( - RibEventEmitter.javaClass.name, - RibEventEmitterType, + ribActionEmitter.javaClass.name, + ribActionEmitterType, ribEventType, ribActionState, Thread.currentThread().name, @@ -124,13 +126,23 @@ public object RibEvents { /** Holds relevant RIB event information */ public data class RibActionInfo( - /** Related RIB class name */ - val ribEventEmitterName: String, + /** Related RIB Action concrete class name */ + val ribActionEmitterName: String, - /** The current RIB event type being bound (e.g. Interactor/Presenter/Router) */ - val RibEventEmitterType: RibEventEmitterType, + /** The current RIB event type being bound (e.g. INTERACTOR/PRESENTER/ROUTER/WORKER) */ + val ribActionEmitterType: RibActionEmitterType, - /** Represents the RIB event type, e.g. ATTACHED/DETACHED */ + /** + * Represents the RIB event type (ATTACHED/DETACHED). + * + * For example for interactor: + * - Interactor.didBecomeActive -> ATTACHED + * - Interactor.willResignActive -> DETACHED + * + * For Worker: + * - Worker.onStart() -> ATTACHED + * - Worker.onStop() -> DETACHED + */ val ribEventType: RibEventType, /** RIB Action state (e.g. event to be called before/after didBecomeActive, willLoad, etc) */ @@ -141,12 +153,12 @@ public data class RibActionInfo( ) /** - * Contract for all related Rib Components e.g. Interactor, Presenter, Router, Workers where will be - * emitting via [ribActionEvents] + * Contract for all related Rib Action Types (Interactor, Presenter, Router, Worker) where will be + * emitting via [ribActionEvents] observable */ -public interface RibEventEmitter +public interface RibActionEmitter -public enum class RibEventEmitterType { +public enum class RibActionEmitterType { ROUTER, PRESENTER, INTERACTOR, 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 5c7bc4bd6..0e381bbc6 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 @@ -34,7 +34,7 @@ protected constructor( public open val interactor: I, private val ribRefWatcher: RibRefWatcher, private val mainThread: Thread, -) : RibEventEmitter { +) : RibActionEmitter { private val children: MutableList> = CopyOnWriteArrayList() private val interactorGeneric: Interactor<*, *> get() = interactor as Interactor<*, *> @@ -117,7 +117,7 @@ protected constructor( triggerRibActionAndEmitEvents( childRouter, - RibEventEmitterType.ROUTER, + RibActionEmitterType.ROUTER, RibEventType.ATTACHED, ) { children.add(childRouter) @@ -169,7 +169,7 @@ protected constructor( triggerRibActionAndEmitEvents( childRouter, - RibEventEmitterType.ROUTER, + RibActionEmitterType.ROUTER, RibEventType.DETACHED, ) { childRouter.dispatchDetach() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index 81942f470..4796b65dd 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -38,7 +38,7 @@ import kotlinx.coroutines.CoroutineDispatcher """, replaceWith = ReplaceWith("RibCoroutineWorker"), ) -public interface Worker : RibEventEmitter { +public interface Worker : RibActionEmitter { /** * When overriden, will specify on which [CoroutineContext] the [Worker] will be bound via 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 77a7a46a9..7d4b154a3 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 @@ -267,7 +267,7 @@ private fun > Worker.bind( .onCompletion { triggerRibActionAndEmitEvents( this@bind, - RibEventEmitterType.DEPRECATED_WORKER, + RibActionEmitterType.DEPRECATED_WORKER, RibEventType.DETACHED, ) { onStop() @@ -278,7 +278,7 @@ private fun > Worker.bind( .collect { triggerRibActionAndEmitEvents( this@bind, - RibEventEmitterType.DEPRECATED_WORKER, + RibActionEmitterType.DEPRECATED_WORKER, RibEventType.ATTACHED, ) { onStart(workerScopeProvider) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index d40126c64..c1bbdc736 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -67,7 +67,7 @@ class InteractorAndRouterTest { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibEventEmitterType.INTERACTOR, + RibActionEmitterType.INTERACTOR, RibActionState.COMPLETED, "com.uber.rib.core.InteractorAndRouterTest${'$'}TestInteractor", ) @@ -102,7 +102,7 @@ class InteractorAndRouterTest { .last() .assertRibActionInfo( RibEventType.DETACHED, - RibEventEmitterType.ROUTER, + RibActionEmitterType.ROUTER, RibActionState.COMPLETED, "com.uber.rib.core.FakeRouter", ) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt index acfede728..2833c04d9 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt @@ -20,13 +20,13 @@ import com.google.common.truth.Truth object RibEventsUtils { internal fun RibActionInfo.assertRibActionInfo( expectedRibEventType: RibEventType, - expectedRibEventEmitterType: RibEventEmitterType, + expectedRibActionEmitterType: RibActionEmitterType, ribActionState: RibActionState, ribClassName: String, ) { Truth.assertThat(this.ribEventType).isEqualTo(expectedRibEventType) - Truth.assertThat(this.RibEventEmitterType).isEqualTo(expectedRibEventEmitterType) + Truth.assertThat(this.ribActionEmitterType).isEqualTo(expectedRibActionEmitterType) Truth.assertThat(this.ribActionState).isEqualTo(ribActionState) - Truth.assertThat(this.ribEventEmitterName).isEqualTo(ribClassName) + Truth.assertThat(this.ribActionEmitterName).isEqualTo(ribClassName) } } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index d1576bc36..709756ab7 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -187,7 +187,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibEventEmitterType.DEPRECATED_WORKER, + RibActionEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.FakeWorker", ) @@ -214,7 +214,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.ATTACHED, - RibEventEmitterType.DEPRECATED_WORKER, + RibActionEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.UiWorker", ) @@ -230,7 +230,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { .last() .assertRibActionInfo( RibEventType.DETACHED, - RibEventEmitterType.DEPRECATED_WORKER, + RibActionEmitterType.DEPRECATED_WORKER, RibActionState.COMPLETED, ribClassName = "com.uber.rib.core.FakeWorker", ) From 1df34c7ad68c7428b331d424f11bea6e6a72840f Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 14:48:53 -0700 Subject: [PATCH 28/52] Deprecate WorkerUnbinder --- .../rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt | 2 +- .../src/main/kotlin/com/uber/rib/core/WorkerBinder.kt | 4 ++-- .../src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index b6f378c72..7bee6c291 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.CoroutineDispatcher @Deprecated( message = """ - [com.uber.rib.core.Worker] is deprecated in favor of [com.uber.rib.core.RibCoroutineWorker] + com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker """, replaceWith = ReplaceWith("RibCoroutineWorker"), ) 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 102ce8d6f..0427efbba 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 @@ -47,9 +47,9 @@ private val Worker.bindingCoroutineContext: CoroutineContext @Deprecated( message = """ - [com.uber.rib.core.Worker] is deprecated in favor of [com.uber.rib.core.RibCoroutineWorker] + com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker """, - replaceWith = ReplaceWith("For binding a RibCoroutineWorker use its .bind extension function"), + replaceWith = ReplaceWith("coroutineScope.bind(ribCoroutineWorker)"), ) public object WorkerBinder { diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt index 331ea7fdb..dec0a46c7 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/WorkerUnbinder.kt @@ -19,6 +19,12 @@ package com.uber.rib.core * API for unbinding a [Worker] before currently bound lifecycle has ended. Use this if you need to * stop your [Worker] before the [Interactor] becomes inactive for example. */ +@Deprecated( + message = + """ + com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker + """, +) public fun interface WorkerUnbinder { /** Unbind from bound lifecycle and end worker's lifecycle. */ public fun unbind() From ee3aff2ebdaacebe2f150884e0671ac9409ea822 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 15:53:54 -0700 Subject: [PATCH 29/52] Open areRibActionEmissionsAllowed get visibility --- .../main/ribworkerselection/RibWorkerSelectionInteractor.kt | 5 +++++ .../rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt index 02b28cc4e..16d7f5d95 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt @@ -15,9 +15,11 @@ */ package com.uber.rib.workers.root.main.ribworkerselection +import android.util.Log import com.uber.rib.core.BasicInteractor import com.uber.rib.core.Bundle import com.uber.rib.core.ComposePresenter +import com.uber.rib.core.RibEvents import com.uber.rib.core.WorkerBinder import com.uber.rib.core.asRibCoroutineWorker import com.uber.rib.core.asWorker @@ -46,6 +48,9 @@ class RibWorkerSelectionInteractor( override fun didBecomeActive(savedInstanceState: Bundle?) { super.didBecomeActive(savedInstanceState) + + Log.d(this.javaClass.name, "WorkerLogger enabled? $RibEvents.areRibActionEmissionsAllowed") + eventStream .observe() .onEach { diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 06cceb534..03d119d1f 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -34,7 +34,9 @@ public object RibEvents { @JvmStatic public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() - private var areRibActionEmissionsAllowed = false + /** Indicates if [ribActionEvents] will be emitting. */ + public var areRibActionEmissionsAllowed: Boolean = false + private set /** * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest From 59014ba3936506b70b415b78e41d543a42af5cd6 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 22 Jun 2023 16:43:10 -0700 Subject: [PATCH 30/52] Remove disableRibActionEmissions and use internal setter --- .../RibWorkerSelectionInteractor.kt | 2 +- .../src/main/kotlin/com/uber/rib/core/RibEvents.kt | 12 ++++-------- .../com/uber/rib/core/InteractorAndRouterTest.kt | 2 +- .../kotlin/com/uber/rib/core/WorkerBinderTest.kt | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt index 16d7f5d95..01276d04b 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt @@ -49,7 +49,7 @@ class RibWorkerSelectionInteractor( override fun didBecomeActive(savedInstanceState: Bundle?) { super.didBecomeActive(savedInstanceState) - Log.d(this.javaClass.name, "WorkerLogger enabled? $RibEvents.areRibActionEmissionsAllowed") + Log.d(this.javaClass.name, "WorkerLogger enabled? ${RibEvents.areRibActionEmissionsAllowed}") eventStream .observe() diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt index 03d119d1f..47feba6e4 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibEvents.kt @@ -36,7 +36,7 @@ public object RibEvents { /** Indicates if [ribActionEvents] will be emitting. */ public var areRibActionEmissionsAllowed: Boolean = false - private set + @VisibleForTesting internal set /** * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest @@ -47,11 +47,6 @@ public object RibEvents { this.areRibActionEmissionsAllowed = true } - @VisibleForTesting - internal fun disableRibActionEmissions() { - this.areRibActionEmissionsAllowed = false - } - /** * @param eventType [RibEventType] * @param child [Router] @@ -109,8 +104,9 @@ public object RibEvents { ribActionState: RibActionState, ) { if (!areRibActionEmissionsAllowed) { - // Unless specified explicitly via [RibEvents.allowRibActionEmissions] there is no need - // to create unnecessary objects if no one is observing/collecting RibAction events + // Unless specified explicitly via [RibEvents.enableRibActionEmissions()] there is no need + // to create unnecessary objects if there is no intention on observing/collecting RibAction + // events return } diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index c1bbdc736..a23c365d2 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -77,7 +77,7 @@ class InteractorAndRouterTest { @Test fun attach_withoutAllowingEmissions_shouldNotEmtRibActionEvents() { // Given. - RibEvents.disableRibActionEmissions() + RibEvents.areRibActionEmissionsAllowed = false ribActionEvents.subscribe(ribActionInfoObserver) // When. diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 709756ab7..f49f31969 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -195,7 +195,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Test fun bind_withDisabledRibActionEvents_shouldNotEmitActionEvents() = runTest { - RibEvents.disableRibActionEmissions() + RibEvents.areRibActionEmissionsAllowed = false ribActionEvents.subscribe(ribActionInfoObserver) bindFakeWorker() assertThat(ribActionInfoObserver.values()).isEmpty() From 50277319b0a9c078512c158b18c73f4d74fea586 Mon Sep 17 00:00:00 2001 From: tys Date: Fri, 23 Jun 2023 15:06:44 -0700 Subject: [PATCH 31/52] Prepare for release 0.15.0 --- CHANGELOG.md | 14 +++++++++++++- README.md | 6 +++--- android/gradle.properties | 2 +- android/libraries/rib-coroutines-test/README.md | 2 +- android/libraries/rib-coroutines/README.md | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a829938f3..e04257e40 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,18 @@ * [Android] Open lifecycleFlow, thus enabling it for mocking * [Android] [WorkerBinder] Guard against potential Worker.coroutineContext being null while using Mockito -### Version 0.14.2i +### Version 0.14.2 * [Android] Fix potential for deadlocks in `Worker` binding. by @psteiger in https://github.com/uber/RIBs/pull/582 * [Android] Add Rib Worker demo app by @FranAguilera in https://github.com/uber/RIBs/pull/575 + +### Version 0.15.0 +* Only complete the worker's scope after calling `Worker.onStop` by @psteiger in https://github.com/uber/RIBs/pull/585 +* Improve KDoc on `ActivityLifecycleEvent` by explaining ordering semantics. by @psteiger in https://github.com/uber/RIBs/pull/586 +* Make use of `jvmToolchain` for building the project. by @psteiger in https://github.com/uber/RIBs/pull/583 +* Revamp Gradle scripts by @psteiger in https://github.com/uber/RIBs/pull/588 +* Deprecate old worker by @FranAguilera in https://github.com/uber/RIBs/pull/597 +* Allow overriding default CoroutineDispatcher for WorkerBinder calls by @FranAguilera in https://github.com/uber/RIBs/pull/596 +* Update README.md by @FranAguilera in https://github.com/uber/RIBs/pull/600 +* Deprecate WorkerUnbinder by @FranAguilera in https://github.com/uber/RIBs/pull/601 +* Expose ribActionEvents stream by @FranAguilera in https://github.com/uber/RIBs/pull/599 + diff --git a/README.md b/README.md index 23b4a8d28..0f20cfbaa 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ To integrate the recommended minimum setup for RIBs add the following to your `b ```gradle dependencies { - annotationProcessor 'com.uber.rib:rib-compiler-test:0.14.2' - implementation 'com.uber.rib:rib-android:0.14.2' - testImplementation 'com.uber.rib:rib-test:0.14.2' + annotationProcessor 'com.uber.rib:rib-compiler-test:0.15.0' + implementation 'com.uber.rib:rib-android:0.15.0' + testImplementation 'com.uber.rib:rib-test:0.15.0' } ``` There are a number of extension packages available as well including Kotlin extensions, Jetpack Compose support, Coroutines support diff --git a/android/gradle.properties b/android/gradle.properties index ca9f81400..7b996d05d 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.14.3-SNAPSHOT +VERSION_NAME=0.15.0 POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ diff --git a/android/libraries/rib-coroutines-test/README.md b/android/libraries/rib-coroutines-test/README.md index bd241274d..59e596444 100644 --- a/android/libraries/rib-coroutines-test/README.md +++ b/android/libraries/rib-coroutines-test/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines test utils for the rib-co ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines-test:0.14.2' + implementation 'com.uber.rib:rib-coroutines-test:0.15.0' } ``` diff --git a/android/libraries/rib-coroutines/README.md b/android/libraries/rib-coroutines/README.md index 688659bd8..52c811b5b 100644 --- a/android/libraries/rib-coroutines/README.md +++ b/android/libraries/rib-coroutines/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines extensions for the rib-ba ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines:0.14.2' + implementation 'com.uber.rib:rib-coroutines:0.15.0' } ``` From c7b7555eb5848c4556a10a5321bb3e8c2ef918db Mon Sep 17 00:00:00 2001 From: tys Date: Fri, 23 Jun 2023 15:12:46 -0700 Subject: [PATCH 32/52] Prepare next development version --- android/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle.properties b/android/gradle.properties index 7b996d05d..ced33a914 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.15.0 +VERSION_NAME=0.15.1-SNAPSHOT POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ From d945defd6bd34c17bd7c81357ef1180224035084 Mon Sep 17 00:00:00 2001 From: James Barr Date: Tue, 11 Jul 2023 11:00:51 -0700 Subject: [PATCH 33/52] Remove unused ids --- android/demos/compose/src/main/res/values/ids.xml | 1 - android/demos/rib-workers/src/main/res/values/ids.xml | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 android/demos/rib-workers/src/main/res/values/ids.xml diff --git a/android/demos/compose/src/main/res/values/ids.xml b/android/demos/compose/src/main/res/values/ids.xml index 21ffa0bf3..55344e519 100644 --- a/android/demos/compose/src/main/res/values/ids.xml +++ b/android/demos/compose/src/main/res/values/ids.xml @@ -1,4 +1,3 @@ - \ No newline at end of file diff --git a/android/demos/rib-workers/src/main/res/values/ids.xml b/android/demos/rib-workers/src/main/res/values/ids.xml deleted file mode 100644 index 21ffa0bf3..000000000 --- a/android/demos/rib-workers/src/main/res/values/ids.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From 36d2c1364755afa210521266f7f680074e3c39aa Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Wed, 10 May 2023 11:19:14 -0300 Subject: [PATCH 34/52] Migrate from deprecated @JvmDefault to compiler option `-Xjvm-default=all` Docs state that: > If you used the @JvmDefault annotation before, you can safely remove it and use one of the new modes. If you already used -Xjvm-default=enable, which generated only the default method implementations, you can now replace it with -Xjvm-default=all. --- ...tlin-android-application-conventions.gradle.kts | 2 +- ...s.kotlin-android-library-conventions.gradle.kts | 2 +- .../ribs.kotlin-library-conventions.gradle.kts | 2 +- .../kotlin/com/uber/rib/core/ActivityDelegate.kt | 14 ++++++-------- .../kotlin/com/uber/rib/core/RxActivityEvents.kt | 2 -- .../kotlin/com/uber/rib/core/RibCoroutineWorker.kt | 2 +- .../src/main/kotlin/com/uber/rib/core/Worker.kt | 5 ++--- .../com/uber/rib/core/RouterNavigatorState.kt | 3 +-- 8 files changed, 13 insertions(+), 19 deletions(-) diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts index 5b92437da..fbfd26dec 100644 --- a/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts +++ b/android/conventions/src/main/kotlin/ribs.kotlin-android-application-conventions.gradle.kts @@ -72,7 +72,7 @@ androidComponents { tasks.withType().configureEach { compilerOptions { freeCompilerArgs.addAll( - "-Xjvm-default=enable", + "-Xjvm-default=all", "-opt-in=kotlin.RequiresOptIn", ) } diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts index 19a2336a2..94202bde7 100644 --- a/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts +++ b/android/conventions/src/main/kotlin/ribs.kotlin-android-library-conventions.gradle.kts @@ -69,7 +69,7 @@ tasks.withType().configureEach { compilerOptions { freeCompilerArgs.addAll( "-Xexplicit-api=warning", - "-Xjvm-default=enable", + "-Xjvm-default=all", "-opt-in=kotlin.RequiresOptIn", ) } diff --git a/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts b/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts index f26c4a37a..4f7b90d01 100644 --- a/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts +++ b/android/conventions/src/main/kotlin/ribs.kotlin-library-conventions.gradle.kts @@ -25,6 +25,6 @@ kotlin { tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask::class.java) { compilerOptions { - freeCompilerArgs.add("-Xjvm-default=enable") + freeCompilerArgs.add("-Xjvm-default=all") } } diff --git a/android/libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/ActivityDelegate.kt b/android/libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/ActivityDelegate.kt index 193826e99..c2b391621 100644 --- a/android/libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/ActivityDelegate.kt +++ b/android/libraries/rib-android-core/src/main/kotlin/com/uber/rib/core/ActivityDelegate.kt @@ -27,25 +27,24 @@ import androidx.annotation.IntRange */ interface ActivityDelegate { /** @see [Activity.onCreate] */ - @JvmDefault fun onCreate(savedInstanceState: Bundle?) {} + fun onCreate(savedInstanceState: Bundle?) {} /** @see [Activity.onStart] */ - @JvmDefault fun onStart() {} + fun onStart() {} /** @see [Activity.onResume] */ - @JvmDefault fun onResume() {} + fun onResume() {} /** @see [Activity.onPause] */ - @JvmDefault fun onPause() {} + fun onPause() {} /** @see [Activity.onStop] */ - @JvmDefault fun onStop() {} + fun onStop() {} /** @see [Activity.onDestroy] */ - @JvmDefault fun onDestroy() {} + fun onDestroy() {} /** @see [Activity.onActivityResult] */ - @JvmDefault fun onActivityResult( activity: Activity, requestCode: Int, @@ -54,7 +53,6 @@ interface ActivityDelegate { ) {} /** @see [Activity.onRequestPermissionsResult] */ - @JvmDefault fun onRequestPermissionsResult( activity: Activity, @IntRange(from = 0, to = 255) requestCode: Int, diff --git a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RxActivityEvents.kt b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RxActivityEvents.kt index 7d3e77529..1e83cdfd3 100644 --- a/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RxActivityEvents.kt +++ b/android/libraries/rib-android/src/main/kotlin/com/uber/rib/core/RxActivityEvents.kt @@ -32,7 +32,6 @@ interface RxActivityEvents { * @param clazz The [ActivityLifecycleEvent] subclass you want. * @return an observable of this activity's lifecycle events. */ - @JvmDefault fun lifecycle(clazz: Class): Observable { return lifecycle() .filter { activityEvent -> clazz.isAssignableFrom(activityEvent.javaClass) } @@ -44,7 +43,6 @@ interface RxActivityEvents { * @param clazz The [ActivityCallbackEvent] subclass you want. * @return an observable of this activity's callbacks events. */ - @JvmDefault fun callbacks(clazz: Class): Observable { return callbacks() .filter { activityEvent -> clazz.isAssignableFrom(activityEvent.javaClass) } diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index 8fa735925..ddeb57e17 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -49,7 +49,7 @@ public fun interface RibCoroutineWorker : RibActionEmitter { public suspend fun onStart(scope: CoroutineScope) /** Called when worker is stopped with [cause]. Should be fast, be non-blocking and not throw. */ - @JvmDefault public fun onStop(cause: Throwable) {} + public fun onStop(cause: Throwable) {} } // ---- Binder ---- // diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt index 8bf3e657a..f97a5d2a3 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/Worker.kt @@ -50,7 +50,6 @@ public interface Worker : RibActionEmitter { * 3) After calling WorkerBinder.bind(interactor, workers, RibDispatchers.IO). [uiWorker] will be * guaranteed to be called on RibDispatchers.Main */ - @JvmDefault public val coroutineContext: CoroutineContext get() = EmptyCoroutineContext @@ -59,8 +58,8 @@ public interface Worker : RibActionEmitter { * * @param lifecycle The lifecycle of the worker to use for subscriptions. */ - @JvmDefault public fun onStart(lifecycle: WorkerScopeProvider) {} + public fun onStart(lifecycle: WorkerScopeProvider) {} /** Called when the worker is stopped. */ - @JvmDefault public fun onStop() {} + public fun onStop() {} } diff --git a/android/libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorState.kt b/android/libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorState.kt index 2ffe1ad6c..9dd80a4b8 100644 --- a/android/libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorState.kt +++ b/android/libraries/rib-router-navigator/src/main/kotlin/com/uber/rib/core/RouterNavigatorState.kt @@ -19,7 +19,6 @@ package com.uber.rib.core public interface RouterNavigatorState { /** @return identifier for a [StackRouterNavigator] state. */ - @JvmDefault public fun stateName(): String { return if (this.javaClass.isEnum) { (this as Enum<*>).name @@ -39,5 +38,5 @@ public interface RouterNavigatorState { * [RouterNavigator.DetachCallback.onPostDetachFromHost] and will be recreated for next * [RouterNavigator.AttachTransition] */ - @JvmDefault public fun isCacheable(): Boolean = false + public fun isCacheable(): Boolean = false } From 5835f8e5b37865cd83d5baaef4e92aad94022b8a Mon Sep 17 00:00:00 2001 From: James Barr Date: Mon, 17 Jul 2023 17:04:00 -0700 Subject: [PATCH 35/52] Prepare for release 0.15.1 --- CHANGELOG.md | 3 +++ README.md | 6 +++--- android/gradle.properties | 2 +- android/libraries/rib-coroutines-test/README.md | 2 +- android/libraries/rib-coroutines/README.md | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e04257e40..fb166127e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,3 +103,6 @@ * Deprecate WorkerUnbinder by @FranAguilera in https://github.com/uber/RIBs/pull/601 * Expose ribActionEvents stream by @FranAguilera in https://github.com/uber/RIBs/pull/599 +### Version 0.15.1 +* [Android] Remove use of @JvmDefault in favor of using -Xjvm-default=all by @psteiger in https://github.com/uber/RIBs/pull/576 + diff --git a/README.md b/README.md index 0f20cfbaa..ae1d1e98b 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ To integrate the recommended minimum setup for RIBs add the following to your `b ```gradle dependencies { - annotationProcessor 'com.uber.rib:rib-compiler-test:0.15.0' - implementation 'com.uber.rib:rib-android:0.15.0' - testImplementation 'com.uber.rib:rib-test:0.15.0' + annotationProcessor 'com.uber.rib:rib-compiler-test:0.15.1' + implementation 'com.uber.rib:rib-android:0.15.1' + testImplementation 'com.uber.rib:rib-test:0.15.1' } ``` There are a number of extension packages available as well including Kotlin extensions, Jetpack Compose support, Coroutines support diff --git a/android/gradle.properties b/android/gradle.properties index ced33a914..c78820c99 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.15.1-SNAPSHOT +VERSION_NAME=0.15.1 POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ diff --git a/android/libraries/rib-coroutines-test/README.md b/android/libraries/rib-coroutines-test/README.md index 59e596444..91824876d 100644 --- a/android/libraries/rib-coroutines-test/README.md +++ b/android/libraries/rib-coroutines-test/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines test utils for the rib-co ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines-test:0.15.0' + implementation 'com.uber.rib:rib-coroutines-test:0.15.1' } ``` diff --git a/android/libraries/rib-coroutines/README.md b/android/libraries/rib-coroutines/README.md index 52c811b5b..f40413262 100644 --- a/android/libraries/rib-coroutines/README.md +++ b/android/libraries/rib-coroutines/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines extensions for the rib-ba ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines:0.15.0' + implementation 'com.uber.rib:rib-coroutines:0.15.1' } ``` From 2de8f9090d52d8f453d7104197e6a2c7fb5493a3 Mon Sep 17 00:00:00 2001 From: James Barr Date: Mon, 17 Jul 2023 17:08:37 -0700 Subject: [PATCH 36/52] Prepare next development version --- android/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle.properties b/android/gradle.properties index c78820c99..c403dc3ce 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.15.1 +VERSION_NAME=0.15.2-SNAPSHOT POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ From 0d87f610e9fd7fe4c3abc661593e0c30be1332d3 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Tue, 25 Jul 2023 12:07:36 -0300 Subject: [PATCH 37/52] Add `WorkerBinder.bind` overloads that take in an `Iterable`. `List` is not really needed and we restrict the API unnecessarily: all we need is an `Iterable`. For keeping binary compatibility, we also keep the overloads taking in a `List`. --- .../kotlin/com/uber/rib/core/WorkerBinder.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) 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 c035a3f64..d63ae1941 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 @@ -83,6 +83,22 @@ public object WorkerBinder { public fun bind( interactor: Interactor<*, *>, workers: List, + ) { + bind(interactor, workers as Iterable) + } + + /** + * Bind workers (i.e. a manager or any other class that needs an interactor lifecycle) to an + * interactor lifecycle events. Use this class into your interactor and call this method on + * attach. + * + * @param interactor The interactor that provides the lifecycle. + * @param workers A list of classes that want to be informed when to start and stop doing work. + */ + @JvmStatic + public fun bind( + interactor: Interactor<*, *>, + workers: Iterable, ) { for (interactorWorker in workers) { bind(interactor, interactorWorker) @@ -119,6 +135,22 @@ public object WorkerBinder { public fun bind( presenter: Presenter, workers: List, + ) { + bind(presenter, workers as Iterable) + } + + /** + * Bind a list of workers (i.e. a manager or any other class that needs a presenter lifecycle) to + * a presenter lifecycle events. Use this class into your presenter and call this method on + * attach. + * + * @param presenter The presenter that provides the lifecycle. + * @param workers A list of classes that want to be informed when to start and stop doing work. + */ + @JvmStatic + public fun bind( + presenter: Presenter, + workers: Iterable, ) { for (worker in workers) { bind(presenter, worker) From 9f6a21520a9c22dd5f9df8927b13c43ada7832ca Mon Sep 17 00:00:00 2001 From: James Barr Date: Mon, 7 Aug 2023 15:05:24 -0700 Subject: [PATCH 38/52] Set view tree owners (#606) * Set view tree owners in RibActivity#onCreate * Cleanup duplicate setting of view tree owners * Add more assertions --- .../com/uber/rib/compose/root/RootScope.kt | 9 ++------ .../uber/rib/compose/root/main/MainScope.kt | 14 ++--------- .../com/uber/rib/workers/root/RootScope.kt | 10 ++------ .../uber/rib/workers/root/main/MainScope.kt | 14 ++--------- android/gradle/libs.versions.toml | 3 ++- android/libraries/rib-android/build.gradle | 1 + .../kotlin/com/uber/rib/core/RibActivity.kt | 16 +++++++++++++ .../com/uber/rib/core/RibActivityTest.kt | 23 +++++++++++++++++++ 8 files changed, 50 insertions(+), 40 deletions(-) diff --git a/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootScope.kt b/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootScope.kt index 78ae6445f..8cb96553c 100644 --- a/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootScope.kt +++ b/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/RootScope.kt @@ -16,8 +16,6 @@ package com.uber.rib.compose.root import android.view.ViewGroup -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.uber.rib.compose.root.main.MainScope import com.uber.rib.compose.util.AnalyticsClient import com.uber.rib.compose.util.AnalyticsClientImpl @@ -43,11 +41,8 @@ interface RootScope { abstract fun presenter(): EmptyPresenter - fun view(parentViewGroup: ViewGroup, activity: RibActivity): RootView { - return RootView(parentViewGroup.context).apply { - setViewTreeLifecycleOwner(activity) - setViewTreeSavedStateRegistryOwner(activity) - } + fun view(parentViewGroup: ViewGroup): RootView { + return RootView(parentViewGroup.context) } @Expose diff --git a/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainScope.kt b/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainScope.kt index 2dbb47160..c167261e6 100644 --- a/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainScope.kt +++ b/android/demos/compose/src/main/kotlin/com/uber/rib/compose/root/main/MainScope.kt @@ -19,15 +19,12 @@ import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.ui.platform.ComposeView -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.uber.rib.compose.root.main.loggedin.LoggedInScope import com.uber.rib.compose.root.main.loggedout.LoggedOutScope import com.uber.rib.compose.util.AnalyticsClient import com.uber.rib.compose.util.ExperimentClient import com.uber.rib.compose.util.LoggerClient import com.uber.rib.core.ComposePresenter -import com.uber.rib.core.RibActivity import motif.Expose @motif.Scope @@ -55,15 +52,8 @@ interface MainScope { } } - fun view( - parentViewGroup: ViewGroup, - activity: RibActivity, - presenter: ComposePresenter, - ): ComposeView { - return ComposeView(parentViewGroup.context).apply { - setViewTreeLifecycleOwner(activity) - setViewTreeSavedStateRegistryOwner(activity) - } + fun view(parentViewGroup: ViewGroup): ComposeView { + return ComposeView(parentViewGroup.context) } abstract fun childContent(): MainRouter.ChildContent diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt index e5e025913..e7228f698 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/RootScope.kt @@ -16,10 +16,7 @@ package com.uber.rib.workers.root import android.view.ViewGroup -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.uber.rib.core.EmptyPresenter -import com.uber.rib.core.RibActivity import com.uber.rib.workers.root.main.MainScope @motif.Scope @@ -36,11 +33,8 @@ interface RootScope { abstract fun presenter(): EmptyPresenter - fun view(parentViewGroup: ViewGroup, activity: RibActivity): RootView { - return RootView(parentViewGroup.context).apply { - setViewTreeLifecycleOwner(activity) - setViewTreeSavedStateRegistryOwner(activity) - } + fun view(parentViewGroup: ViewGroup): RootView { + return RootView(parentViewGroup.context) } } } diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainScope.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainScope.kt index 3eeed360e..08b05822b 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainScope.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/MainScope.kt @@ -19,10 +19,7 @@ import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.ui.platform.ComposeView -import androidx.lifecycle.setViewTreeLifecycleOwner -import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.uber.rib.core.ComposePresenter -import com.uber.rib.core.RibActivity import com.uber.rib.workers.root.main.ribworkerselection.RibWorkerSelectionScope @motif.Scope @@ -45,15 +42,8 @@ interface MainScope { } } - fun view( - parentViewGroup: ViewGroup, - activity: RibActivity, - presenter: ComposePresenter, - ): ComposeView { - return ComposeView(parentViewGroup.context).apply { - setViewTreeLifecycleOwner(activity) - setViewTreeSavedStateRegistryOwner(activity) - } + fun view(parentViewGroup: ViewGroup): ComposeView { + return ComposeView(parentViewGroup.context) } abstract fun childContent(): MainRouter.ChildContent diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 4362e9552..5b19dcca5 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -3,7 +3,7 @@ android-api = "4.1.1.4" androidx-activity = "1.7.0" androidx-annotations = "1.1.0" androidx-appcompat = "1.3.0" -androidx-lifecycle = "2.5.1" +androidx-lifecycle = "2.6.1" autocommon = "0.8" autodispose = "1.4.0" autoservice = "1.0-rc4" @@ -95,6 +95,7 @@ javax-inject = { group = "javax.inject", name = "javax.inject", version = "1" } jsr250 = { group = "javax.annotation", name = "jsr250-api", version.ref = "jsr250" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" } +lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidx-lifecycle" } motif-compiler = { group = "com.uber.motif", name = "motif-compiler", version.ref = "motif" } motif-library = { group = "com.uber.motif", name = "motif", version.ref = "motif" } percent = { group = "androidx.percentlayout", name = "percentlayout", version.ref = "percent" } diff --git a/android/libraries/rib-android/build.gradle b/android/libraries/rib-android/build.gradle index 4c6406eea..17f04c47e 100644 --- a/android/libraries/rib-android/build.gradle +++ b/android/libraries/rib-android/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation(libs.coroutines.android) implementation(libs.coroutines.rx2) testImplementation(testLibs.robolectric) + testImplementation(libs.lifecycle.runtime) testImplementation(libs.appcompat) testImplementation(testLibs.mockitoKotlin) testImplementation(project(":libraries:rib-test")) 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 04400d736..41900f3a8 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 @@ -22,6 +22,9 @@ import android.content.res.Configuration import android.os.Build import android.view.ViewGroup import androidx.annotation.CallSuper +import androidx.lifecycle.ViewTreeLifecycleOwner +import androidx.lifecycle.ViewTreeViewModelStoreOwner +import androidx.savedstate.ViewTreeSavedStateRegistryOwner import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import com.uber.autodispose.lifecycle.LifecycleEndedException import com.uber.autodispose.lifecycle.LifecycleNotStartedException @@ -90,6 +93,7 @@ abstract class RibActivity : @CallSuper override fun onCreate(savedInstanceState: android.os.Bundle?) { super.onCreate(savedInstanceState) + initViewTreeOwners() val rootViewGroup = findViewById(android.R.id.content) _lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState)) val wrappedBundle: Bundle? = @@ -232,6 +236,18 @@ abstract class RibActivity : */ protected abstract fun createRouter(parentViewGroup: ViewGroup): ViewRouter<*, *> + /** + * [RibActivity] must call this since it does not use [ComponentActivity.setContentView] which + * already handles this. + */ + private fun initViewTreeOwners() { + // Set the view tree owners before setting the content view so that the inflation process + // and attach listeners will see them already present + ViewTreeLifecycleOwner.set(window.decorView, this) + ViewTreeViewModelStoreOwner.set(window.decorView, this) + ViewTreeSavedStateRegistryOwner.set(window.decorView, this) + } + companion object { /** * Figures out which corresponding next lifecycle event in which to unsubscribe, for Activities. diff --git a/android/libraries/rib-android/src/test/kotlin/com/uber/rib/core/RibActivityTest.kt b/android/libraries/rib-android/src/test/kotlin/com/uber/rib/core/RibActivityTest.kt index 55523e2d7..87f7ddc2f 100644 --- a/android/libraries/rib-android/src/test/kotlin/com/uber/rib/core/RibActivityTest.kt +++ b/android/libraries/rib-android/src/test/kotlin/com/uber/rib/core/RibActivityTest.kt @@ -20,6 +20,9 @@ import android.content.Intent import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.lifecycle.findViewTreeLifecycleOwner +import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner import com.google.common.truth.Truth.assertThat import com.uber.autodispose.AutoDispose import com.uber.autodispose.lifecycle.LifecycleEndedException @@ -189,6 +192,26 @@ class RibActivityTest { assertThat(activity.interactor).isNotNull() } + @Test + fun onCreate_setsViewTreeOwners_forDecorView() { + val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get() + val decorView = activity.window.decorView + assertThat(decorView.findViewTreeLifecycleOwner()).isNotNull() + assertThat(decorView.findViewTreeSavedStateRegistryOwner()).isNotNull() + assertThat(decorView.findViewTreeViewModelStoreOwner()).isNotNull() + } + + @Test + fun onCreate_setsViewTreeOwners_forViewAddedToDecorView() { + val activity = Robolectric.buildActivity(EmptyActivity::class.java).create(null).get() + val contentView = activity.findViewById(android.R.id.content) + val rootView = View(RuntimeEnvironment.application) + contentView.addView(rootView) + assertThat(rootView.findViewTreeLifecycleOwner()).isNotNull() + assertThat(rootView.findViewTreeSavedStateRegistryOwner()).isNotNull() + assertThat(rootView.findViewTreeViewModelStoreOwner()).isNotNull() + } + @Test(expected = IllegalArgumentException::class) fun createEvent_withIllegalType_shouldFail() { create(ActivityLifecycleEvent.Type.CREATE) From 0ea5c5c7153d81f026500e41f6fadf4e35830287 Mon Sep 17 00:00:00 2001 From: James Barr Date: Mon, 7 Aug 2023 15:09:24 -0700 Subject: [PATCH 39/52] Prepare for release 0.15.2 --- CHANGELOG.md | 3 +++ README.md | 6 +++--- android/gradle.properties | 2 +- android/libraries/rib-coroutines-test/README.md | 2 +- android/libraries/rib-coroutines/README.md | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb166127e..df7c08494 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,3 +106,6 @@ ### Version 0.15.1 * [Android] Remove use of @JvmDefault in favor of using -Xjvm-default=all by @psteiger in https://github.com/uber/RIBs/pull/576 +### Version 0.15.2 +* [Android] Set view tree owners for RibActivity + diff --git a/README.md b/README.md index ae1d1e98b..e83db3668 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ To integrate the recommended minimum setup for RIBs add the following to your `b ```gradle dependencies { - annotationProcessor 'com.uber.rib:rib-compiler-test:0.15.1' - implementation 'com.uber.rib:rib-android:0.15.1' - testImplementation 'com.uber.rib:rib-test:0.15.1' + annotationProcessor 'com.uber.rib:rib-compiler-test:0.15.2' + implementation 'com.uber.rib:rib-android:0.15.2' + testImplementation 'com.uber.rib:rib-test:0.15.2' } ``` There are a number of extension packages available as well including Kotlin extensions, Jetpack Compose support, Coroutines support diff --git a/android/gradle.properties b/android/gradle.properties index c403dc3ce..51eadeeee 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.15.2-SNAPSHOT +VERSION_NAME=0.15.2 POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ diff --git a/android/libraries/rib-coroutines-test/README.md b/android/libraries/rib-coroutines-test/README.md index 91824876d..4cbcbca5d 100644 --- a/android/libraries/rib-coroutines-test/README.md +++ b/android/libraries/rib-coroutines-test/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines test utils for the rib-co ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines-test:0.15.1' + implementation 'com.uber.rib:rib-coroutines-test:0.15.2' } ``` diff --git a/android/libraries/rib-coroutines/README.md b/android/libraries/rib-coroutines/README.md index f40413262..d299b6d44 100644 --- a/android/libraries/rib-coroutines/README.md +++ b/android/libraries/rib-coroutines/README.md @@ -5,7 +5,7 @@ This module is responsible for defining the coroutines extensions for the rib-ba ## Installation ```gradle dependencies { - implementation 'com.uber.rib:rib-coroutines:0.15.1' + implementation 'com.uber.rib:rib-coroutines:0.15.2' } ``` From 484a476c995d4516524e78c4ce6daf6d550a95b1 Mon Sep 17 00:00:00 2001 From: James Barr Date: Mon, 7 Aug 2023 15:14:04 -0700 Subject: [PATCH 40/52] Prepare next development version --- android/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle.properties b/android/gradle.properties index 51eadeeee..e64d2de25 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -13,7 +13,7 @@ # org.gradle.parallel=true GROUP=com.uber.rib -VERSION_NAME=0.15.2 +VERSION_NAME=0.15.3-SNAPSHOT POM_DESCRIPTION=RIBs is the cross-platform architecture behind many mobile apps at Uber. This framework is designed for mobile apps with a large number of engineers and nested states. POM_URL=https://github.com/uber/RIBs/ POM_SCM_URL=https://github.com/uber/RIBs/ From d719af07def6a74d8edb18d265b06eb2a795ead4 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 10 Aug 2023 09:33:44 -0700 Subject: [PATCH 41/52] Add RibCoroutineWorker.bind receiving multiple workers --- .../ribworkerselection/RibWorkerBindTypeClickType.kt | 3 ++- .../RibWorkerSelectionInteractor.kt | 9 +++++++-- .../main/ribworkerselection/RibWorkerSelectionView.kt | 10 ++++++++-- .../kotlin/com/uber/rib/core/RibCoroutineWorker.kt | 11 +++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerBindTypeClickType.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerBindTypeClickType.kt index e0b14d722..dfac9f0dc 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerBindTypeClickType.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerBindTypeClickType.kt @@ -18,7 +18,8 @@ package com.uber.rib.workers.root.main.ribworkerselection enum class RibWorkerBindTypeClickType { SINGLE_WORKER_BIND_CALLER_THREAD, SINGLE_WORKER_BIND_BACKGROUND_THREAD, - BIND_MULTIPLE_WORKERS, + BIND_MULTIPLE_DEPRECATED_WORKERS, + BIND_MULTIPLE_RIB_COROUTINE_WORKERS, BIND_RIB_COROUTINE_WORKER, WORKER_TO_RIB_COROUTINE_WORKER, RIB_COROUTINE_WORKER_TO_WORKER, diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt index 01276d04b..4a94dfa08 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionInteractor.kt @@ -63,11 +63,16 @@ class RibWorkerSelectionInteractor( updateViewModel(ioWorker::class.simpleName) WorkerBinder.bind(this, ioWorker) } - RibWorkerBindTypeClickType.BIND_MULTIPLE_WORKERS -> { + RibWorkerBindTypeClickType.BIND_MULTIPLE_DEPRECATED_WORKERS -> { val workers = listOf(backgroundWorker, defaultWorker, ioWorker, uiWorker) - updateViewModel("Multiple workers ") + updateViewModel("Multiple deprecated workers ") WorkerBinder.bind(this, workers) } + RibWorkerBindTypeClickType.BIND_MULTIPLE_RIB_COROUTINE_WORKERS -> { + val workers = listOf(defaultRibCoroutineWorker, defaultRibCoroutineWorker) + updateViewModel("Multiple RibCoroutineWorkers ") + coroutineScope.bind(workers) + } RibWorkerBindTypeClickType.BIND_RIB_COROUTINE_WORKER -> { updateViewModel(defaultRibCoroutineWorker::class.simpleName) coroutineScope.bind(defaultRibCoroutineWorker) diff --git a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionView.kt b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionView.kt index 72a3a5d1d..a1b55db5e 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionView.kt +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/ribworkerselection/RibWorkerSelectionView.kt @@ -57,8 +57,14 @@ fun RibWorkerSelectionView( AddButton( eventStream, - RibWorkerBindTypeClickType.BIND_MULTIPLE_WORKERS, - "Bind multiple workers", + RibWorkerBindTypeClickType.BIND_MULTIPLE_DEPRECATED_WORKERS, + "Bind multiple Deprecated Workers", + ) + + AddButton( + eventStream, + RibWorkerBindTypeClickType.BIND_MULTIPLE_RIB_COROUTINE_WORKERS, + "Bind multiple RibCoroutineWorkers", ) AddButton( diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index ddeb57e17..b4cbfe050 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -107,6 +107,17 @@ public fun CoroutineScope.bind( return BindWorkerHandleImpl(bindJob, unbindJob) } +/** Binds a list of [worker]'s in a scope that is a child of the [CoroutineScope] receiver. */ +@JvmOverloads +public fun CoroutineScope.bind( + workers: Iterable, + coroutineContext: CoroutineContext = RibDispatchers.Default, +) { + for (worker in workers) { + bind(worker, coroutineContext) + } +} + /** * Guarantees to run synchronous [init] block exactly once in an undispatched manner. * From 94fffa83e96598ba1abebb5dc7bc28ac932a5ae9 Mon Sep 17 00:00:00 2001 From: Francisco Aguilera Date: Thu, 10 Aug 2023 10:12:52 -0700 Subject: [PATCH 42/52] Change dispatcher from empty to default for the Worker<>RibCoroutine conversion --- .../src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index ddeb57e17..dc804a136 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -193,7 +193,7 @@ public fun Worker.asRibCoroutineWorker(): RibCoroutineWorker = /** Converts a [RibCoroutineWorker] to a [Worker]. */ @JvmOverloads public fun RibCoroutineWorker.asWorker( - coroutineContext: CoroutineContext = EmptyCoroutineContext, + coroutineContext: CoroutineContext = RibDispatchers.Default, ): Worker = RibCoroutineWorkerToWorkerAdapter(this, coroutineContext) internal open class WorkerToRibCoroutineWorkerAdapter(private val worker: Worker) : From 6ff90ff9a26557c659c3d17424ec19866eefff0e Mon Sep 17 00:00:00 2001 From: Fran Aguilera Date: Thu, 10 Aug 2023 10:39:40 -0700 Subject: [PATCH 43/52] Update android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt Co-authored-by: Patrick Steiger --- .../src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index b4cbfe050..39997d40e 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -107,7 +107,7 @@ public fun CoroutineScope.bind( return BindWorkerHandleImpl(bindJob, unbindJob) } -/** Binds a list of [worker]'s in a scope that is a child of the [CoroutineScope] receiver. */ +/** Binds [workers] in a scope that is a child of the [CoroutineScope] receiver. */ @JvmOverloads public fun CoroutineScope.bind( workers: Iterable, From c0bf8a8113d897973828d3f8bf5237cb63b9c4be Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 14:03:01 -0700 Subject: [PATCH 44/52] Update coroutines 1.7.3 --- android/gradle/libs.versions.toml | 4 ++-- .../kotlin/com/uber/rib/core/LazyBackingPropertyTest.kt | 2 -- .../kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt | 4 +--- .../src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt | 6 ++++-- .../src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt | 2 -- .../kotlin/com/uber/rib/core/TestRibCoroutineScopes.kt | 7 ------- .../main/kotlin/com/uber/rib/core/TestRibDispatchers.kt | 2 -- .../test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt | 2 -- .../test/kotlin/com/uber/rib/core/RibDispatchersTest.kt | 2 -- .../src/test/kotlin/com/uber/rib/core/RibScopesTest.kt | 2 -- 10 files changed, 7 insertions(+), 26 deletions(-) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 5b19dcca5..db0fa935e 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -13,7 +13,7 @@ compile-testing = "0.17" compose-compiler = "1.4.6" compose-libraries = "1.4.0" compose-navigation = "2.4.0-alpha03" -coroutines = "1.6.4" +coroutines = "1.7.3" dagger = "2.43.2" errorprone = "2.3.3" errorprone-javac = "9+181-r4173-1" @@ -33,7 +33,7 @@ javapoet = "1.11.1" jsr250 = "1.0" junit = "4.12" kotlin = "1.8.20" -kotlinx-coroutines = "1.6.4" +kotlinx-coroutines = "1.7.3" ktfmt = "0.43" ktlint = "0.48.2" leakcanary = "1.5.4" 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 dde6f6e64..5ce172019 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 @@ -17,7 +17,6 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat import io.reactivex.Observable -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.produce import kotlinx.coroutines.channels.toList import kotlinx.coroutines.launch @@ -25,7 +24,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.mockito.kotlin.mock -@OptIn(ExperimentalCoroutinesApi::class) class LazyBackingPropertyTest { @Volatile private var _expensiveObject: ExpensiveObject? = null private val expensiveObject diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt index 13a77f6db..3b3e51406 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibCoroutineWorkerTest.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Delay import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Runnable import kotlinx.coroutines.awaitCancellation @@ -49,7 +48,6 @@ import org.junit.Test private const val ON_START_DELAY_DURATION_MILLIS = 100L -@OptIn(ExperimentalCoroutinesApi::class) class RibCoroutineWorkerTest { @get:Rule val coroutineRule = RibCoroutinesRule() private val worker = TestRibCoroutineWorker() @@ -180,7 +178,7 @@ class RibCoroutineWorkerTest { } } -@OptIn(ExperimentalCoroutinesApi::class, InternalCoroutinesApi::class) +@OptIn(InternalCoroutinesApi::class) private class ImmediateDispatcher( scheduler: TestCoroutineScheduler, private val delegate: TestDispatcher = StandardTestDispatcher(scheduler), diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index f49f31969..709ba2f4e 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -138,7 +139,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { } @Test - fun bind_onStartIsCalledEagerly() { + fun bind_onStartIsCalledEagerly() = runTest { val interactor = object : Interactor>() {} var onStartCalled = false val worker = Worker { onStartCalled = true } @@ -148,7 +149,8 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { } @Test - fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() { + @Ignore("Failing silently before Coroutines 1.7, to be investigated") + fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() = runTest { val interactor = object : Interactor>() {} var enteredUnconfined = false val worker = Worker { diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt index aa9b6a2e2..8f052d4af 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt @@ -15,7 +15,6 @@ */ package com.uber.rib.core -import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.rules.TestWatcher import org.junit.runner.Description @@ -23,7 +22,6 @@ import org.junit.runner.Description * RibCoroutinesRule is a Junit TestRule to act as a managed TestCoroutineScope in test and to * facilitate install and cleanup of Test Dispatchers */ -@ExperimentalCoroutinesApi public class RibCoroutinesRule( public val ribDispatchers: TestRibDispatchers = TestRibDispatchers(), ) : TestWatcher() { 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 50215cac1..237be9976 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 @@ -23,12 +23,10 @@ import com.uber.autodispose.coroutinesinterop.autoDispose import io.reactivex.Completable import io.reactivex.CompletableSource import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.test.TestScope -@ExperimentalCoroutinesApi /** returns the [TestScope] override currently installed for testing. */ public val ScopeProvider.testScopeOverride: TestScope? // Due to custom friend path usage, reference to LazyCoroutineScope will stay red in IDE @@ -42,7 +40,6 @@ public val ScopeProvider.testScopeOverride: TestScope? * Overrides [ScopeProvider.coroutineScope] with a [TestScope] with lifecycle integration for * testing. Accessible directly as [TestScope] via [ScopeProvider.TestScopeOverride]. */ -@ExperimentalCoroutinesApi public fun ScopeProvider.enableTestScopeOverride( context: CoroutineContext = SupervisorJob(), ): Unit = synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = asTestScope(context) } @@ -52,7 +49,6 @@ public fun ScopeProvider.disableTestScopeOverride(): Unit = synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = null } /** returns the [TestScope] override currently installed for testing. */ -@ExperimentalCoroutinesApi public val Application.testScopeOverride: TestScope? // Due to custom friend path usage, reference to LazyCoroutineScope will stay red in IDE get() = @@ -65,7 +61,6 @@ public val Application.testScopeOverride: TestScope? * Overrides [ScopeProvider.coroutineScope] with a [TestScope] with lifecycle integration for * testing. Accessible directly as [TestScope] via [ScopeProvider.TestScopeOverride]. */ -@ExperimentalCoroutinesApi public fun Application.enableTestScopeOverride(context: CoroutineContext = SupervisorJob()): Unit = synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = TestScope(context) } @@ -74,13 +69,11 @@ public fun Application.disableTestScopeOverride(): Unit = synchronized(LazyCoroutineScope) { LazyCoroutineScope[this] = null } /** Returns a new [TestScope] from the [ScopeProvider] */ -@ExperimentalCoroutinesApi public fun ScopeProvider.asTestScope(context: CoroutineContext = SupervisorJob()): TestScope { return requestScope().asTestScope(context) } /** Returns a new [TestScope] from the [CompletableSource] */ -@ExperimentalCoroutinesApi public fun CompletableSource.asTestScope(context: CoroutineContext = SupervisorJob()): TestScope { val scope = TestScope(context) Completable.wrap(this).autoDispose(scope).subscribe({ scope.cancel() }) { e -> diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibDispatchers.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibDispatchers.kt index 9a4eef459..4e52f80b8 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibDispatchers.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/TestRibDispatchers.kt @@ -16,7 +16,6 @@ package com.uber.rib.core import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.MainCoroutineDispatcher import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestCoroutineScheduler @@ -25,7 +24,6 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain -@ExperimentalCoroutinesApi public data class TestRibDispatchers( /** * [TestCoroutineScheduler] to be used by all other [TestDispatcher] when using the default diff --git a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt index c2c273a7c..b86786c83 100644 --- a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt +++ b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibCoroutinesRuleTest.kt @@ -16,7 +16,6 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.test.advanceTimeBy @@ -25,7 +24,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) class RibCoroutinesRuleTest { @get:Rule val ribCoroutinesRule = RibCoroutinesRule() diff --git a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibDispatchersTest.kt b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibDispatchersTest.kt index 9641e364f..c19c8d985 100644 --- a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibDispatchersTest.kt +++ b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibDispatchersTest.kt @@ -17,11 +17,9 @@ package com.uber.rib.core import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher import org.junit.Test -@OptIn(ExperimentalCoroutinesApi::class) internal class RibDispatchersTest { @Test diff --git a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibScopesTest.kt b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibScopesTest.kt index 7d79ab35e..16917843f 100644 --- a/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibScopesTest.kt +++ b/android/libraries/rib-coroutines-test/src/test/kotlin/com/uber/rib/core/RibScopesTest.kt @@ -19,7 +19,6 @@ import android.app.Application import com.google.common.truth.Truth.assertThat import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -29,7 +28,6 @@ import org.junit.Rule import org.junit.Test import org.mockito.kotlin.mock -@OptIn(ExperimentalCoroutinesApi::class) internal class RibScopesTest { @get:Rule var rule = RibCoroutinesRule() From 09a08e634dd0d6b179ded16b23592f2eec832542 Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 14:10:02 -0700 Subject: [PATCH 45/52] Fixing test and removing ignore --- .../src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index 709ba2f4e..c46d3adef 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -64,6 +63,8 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { RibEvents.enableRibActionEmissions() + RibCoroutinesConfig.deprecatedWorkerDispatcher = ribCoroutinesRule.ribDispatchers.Unconfined + } @Test @@ -149,7 +150,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { } @Test - @Ignore("Failing silently before Coroutines 1.7, to be investigated") fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() = runTest { val interactor = object : Interactor>() {} var enteredUnconfined = false From 24e3ae7871f74a701a977918f647acf8bd2da463 Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 14:10:46 -0700 Subject: [PATCH 46/52] Fix spotless --- .../src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index c46d3adef..c3cb9ac53 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -64,7 +64,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { fun setUp() { RibEvents.enableRibActionEmissions() RibCoroutinesConfig.deprecatedWorkerDispatcher = ribCoroutinesRule.ribDispatchers.Unconfined - } @Test From 25b8aaf2766179a2d2125ee56d9696ae100ad798 Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 14:33:16 -0700 Subject: [PATCH 47/52] Setting worker dispatcher in test rule --- .../src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt | 1 - .../src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt index c3cb9ac53..a75f0983c 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/WorkerBinderTest.kt @@ -63,7 +63,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Before fun setUp() { RibEvents.enableRibActionEmissions() - RibCoroutinesConfig.deprecatedWorkerDispatcher = ribCoroutinesRule.ribDispatchers.Unconfined } @Test diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt index 8f052d4af..f11e2fadf 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt @@ -26,11 +26,14 @@ public class RibCoroutinesRule( public val ribDispatchers: TestRibDispatchers = TestRibDispatchers(), ) : TestWatcher() { + private var originalDeprecatedWorkerDispatcher = RibCoroutinesConfig.deprecatedWorkerDispatcher override fun starting(description: Description) { ribDispatchers.installTestDispatchers() + RibCoroutinesConfig.deprecatedWorkerDispatcher = ribDispatchers.Unconfined } override fun finished(description: Description) { ribDispatchers.resetTestDispatchers() + RibCoroutinesConfig.deprecatedWorkerDispatcher = originalDeprecatedWorkerDispatcher } } From a6e2109c2913152378bcb1d32fe452ccd0c1264f Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 15:06:23 -0700 Subject: [PATCH 48/52] addressing feedback --- .../src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt index f11e2fadf..6dbfd01a7 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt @@ -15,6 +15,7 @@ */ package com.uber.rib.core +import kotlinx.coroutines.CoroutineDispatcher import org.junit.rules.TestWatcher import org.junit.runner.Description @@ -26,14 +27,15 @@ public class RibCoroutinesRule( public val ribDispatchers: TestRibDispatchers = TestRibDispatchers(), ) : TestWatcher() { - private var originalDeprecatedWorkerDispatcher = RibCoroutinesConfig.deprecatedWorkerDispatcher + private var originalDeprecatedWorkerDispatcher : CoroutineDispatcher? = null override fun starting(description: Description) { ribDispatchers.installTestDispatchers() + originalDeprecatedWorkerDispatcher = RibCoroutinesConfig.deprecatedWorkerDispatcher RibCoroutinesConfig.deprecatedWorkerDispatcher = ribDispatchers.Unconfined } override fun finished(description: Description) { ribDispatchers.resetTestDispatchers() - RibCoroutinesConfig.deprecatedWorkerDispatcher = originalDeprecatedWorkerDispatcher + RibCoroutinesConfig.deprecatedWorkerDispatcher = originalDeprecatedWorkerDispatcher!! } } From fa39761edbee49bceb09e06fa98098e0f1990975 Mon Sep 17 00:00:00 2001 From: tys Date: Tue, 22 Aug 2023 15:07:08 -0700 Subject: [PATCH 49/52] Spotless --- .../src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt index 6dbfd01a7..af0671684 100644 --- a/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt +++ b/android/libraries/rib-coroutines-test/src/main/kotlin/com/uber/rib/core/RibCoroutinesRule.kt @@ -27,7 +27,7 @@ public class RibCoroutinesRule( public val ribDispatchers: TestRibDispatchers = TestRibDispatchers(), ) : TestWatcher() { - private var originalDeprecatedWorkerDispatcher : CoroutineDispatcher? = null + private var originalDeprecatedWorkerDispatcher: CoroutineDispatcher? = null override fun starting(description: Description) { ribDispatchers.installTestDispatchers() originalDeprecatedWorkerDispatcher = RibCoroutinesConfig.deprecatedWorkerDispatcher From 31f4984632225fde30ad6b702faeeab9bc783aef Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Tue, 22 Aug 2023 19:15:34 -0300 Subject: [PATCH 50/52] Add `RibCoroutineWorker` factory method with `CoroutineScope` as receiver. `RibCoroutineWorker` is already a functional interface; the purpose of this builder is to allow consumers to create a `RibCoroutineWorker` with `CoroutineScope` in receiver position. E.g. - Functional interface: ``` RibCoroutineWorker { scope -> scope.launch { ... } } ``` - This factory method: ``` RibCoroutineWorker { launch { ... } } ``` --- .../com/uber/rib/core/RibCoroutineWorker.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt index e66611d5e..ff40ec9a7 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibCoroutineWorker.kt @@ -52,6 +52,27 @@ public fun interface RibCoroutineWorker : RibActionEmitter { public fun onStop(cause: Throwable) {} } +/** A manager or helper class bound to a [CoroutineScope] by using a binder like [bind]. */ +public inline fun RibCoroutineWorker( + crossinline onStart: suspend CoroutineScope.() -> Unit, +): RibCoroutineWorker { + /* + * 'RibCoroutineWorker' is already a functional interface; the purpose of this builder is to allow consumers + * to create a 'RibCoroutineWorker' with 'CoroutineScope' in receiver position. E.g. + * + * Functional interface: + * RibCoroutineWorker { scope -> + * scope.launch { ... } + * } + * + * This factory method: + * RibCoroutineWorker { + * launch { ... } + * } + */ + return RibCoroutineWorker { scope -> scope.onStart() } +} + // ---- Binder ---- // /** From 85cf3b3c8a7f35e8332d52591e711c2704d87ea8 Mon Sep 17 00:00:00 2001 From: Patrick Steiger Date: Tue, 22 Aug 2023 22:17:06 -0300 Subject: [PATCH 51/52] Bump kotlinx.coroutines.test to 1.7.3 --- android/gradle/test-libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/test-libs.versions.toml b/android/gradle/test-libs.versions.toml index 0b9a3c964..0c3d1754c 100644 --- a/android/gradle/test-libs.versions.toml +++ b/android/gradle/test-libs.versions.toml @@ -1,6 +1,6 @@ [versions] compile-testing = "0.17" -coroutines = "1.6.4" +coroutines = "1.7.3" junit = "4.12" mockito = "4.6.1" mockito-kotlin = "4.0.0" From 04a7f082b02bd4fbc6d81abb8c77765fc8c3a3a9 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Ricau Date: Thu, 11 May 2023 14:23:35 -0700 Subject: [PATCH 52/52] Update LeakCanary to 2.10 Also added the now required "description" parameter when watching instances. Fixes #580 --- .../java/com/uber/rib/SampleApplication.java | 17 ++++--------- android/gradle/libs.versions.toml | 2 +- .../kotlin/com/uber/rib/core/RibRefWatcher.kt | 19 ++++++++++++--- .../main/kotlin/com/uber/rib/core/Router.kt | 5 +++- .../uber/rib/core/InteractorAndRouterTest.kt | 8 +++---- .../com/uber/rib/core/RibRefWatcherTest.kt | 24 +++++++++---------- 6 files changed, 41 insertions(+), 34 deletions(-) diff --git a/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java b/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java index 712589905..39c25026a 100644 --- a/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java +++ b/android/demos/memory-leaks/src/main/java/com/uber/rib/SampleApplication.java @@ -16,12 +16,10 @@ package com.uber.rib; import android.app.Application; -import com.squareup.leakcanary.LeakCanary; -import com.squareup.leakcanary.RefWatcher; import com.uber.rib.core.ActivityDelegate; import com.uber.rib.core.HasActivityDelegate; import com.uber.rib.core.RibRefWatcher; -import java.util.concurrent.TimeUnit; +import leakcanary.AppWatcher; public class SampleApplication extends Application implements HasActivityDelegate { @@ -31,24 +29,17 @@ public class SampleApplication extends Application implements HasActivityDelegat public void onCreate() { activityDelegate = new SampleActivityDelegate(); super.onCreate(); - if (!LeakCanary.isInAnalyzerProcess(this)) { - // This process is dedicated to LeakCanary for heap analysis. You should not init your app in - // this process. - installLeakCanary(); - } + installLeakCanary(); } /** Install leak canary for both activities and RIBs. */ private void installLeakCanary() { - final RefWatcher refWatcher = - LeakCanary.refWatcher(this).watchDelay(2, TimeUnit.SECONDS).buildAndInstall(); - LeakCanary.install(this); RibRefWatcher.getInstance() .setReferenceWatcher( new RibRefWatcher.ReferenceWatcher() { @Override - public void watch(Object object) { - refWatcher.watch(object); + public void watch(Object object, String description) { + AppWatcher.INSTANCE.getObjectWatcher().expectWeaklyReachable(object, description); } @Override diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index db0fa935e..54b4c6753 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -36,7 +36,7 @@ kotlin = "1.8.20" kotlinx-coroutines = "1.7.3" ktfmt = "0.43" ktlint = "0.48.2" -leakcanary = "1.5.4" +leakcanary = "2.10" mockito = "4.6.1" mockito-kotlin = "4.0.0" motif = "0.3.4" diff --git a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt index 8ed91004a..6605a1849 100644 --- a/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt +++ b/android/libraries/rib-base/src/main/kotlin/com/uber/rib/core/RibRefWatcher.kt @@ -34,17 +34,30 @@ public open class RibRefWatcher { referenceWatcher = watcher } + @Deprecated( + "Add the description parameter", + replaceWith = ReplaceWith("watchDeletedObject(objectToWatch, description)"), + ) + public open fun watchDeletedObject( + objectToWatch: Any?, + ) { + watchDeletedObject(objectToWatch, "missing description") + } + /** * Watch this object to verify it has no inbound references. * * @param objectToWatch the object to watch. */ - public open fun watchDeletedObject(objectToWatch: Any?) { + public open fun watchDeletedObject( + objectToWatch: Any?, + description: String, + ) { if (objectToWatch == null) { return } if (isLeakCanaryEnabled || uLeakEnabled) { - referenceWatcher?.watch(objectToWatch) + referenceWatcher?.watch(objectToWatch, description) } } @@ -97,7 +110,7 @@ public open class RibRefWatcher { * * @param objectToWatch the object to watch. */ - public fun watch(objectToWatch: Any) + public fun watch(objectToWatch: Any, description: String) /** * Method to pipe breadcrumbs into the Breadcrumb logger. 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 0e381bbc6..86f5e44a9 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 @@ -150,7 +150,10 @@ protected constructor( public open fun detachChild(childRouter: Router<*>) { val isChildRemoved = children.remove(childRouter) val interactor = childRouter.interactor - ribRefWatcher.watchDeletedObject(interactor) + ribRefWatcher.watchDeletedObject( + interactor, + "detached child router ${childRouter.javaClass.name}", + ) ribRefWatcher.logBreadcrumb( "DETACHED", childRouter.javaClass.simpleName, diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt index a23c365d2..cb03e9c59 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/InteractorAndRouterTest.kt @@ -187,13 +187,13 @@ class InteractorAndRouterTest { val childInteractor = TestInteractorB() val childRouter = TestRouterB(childInteractor, component) router.attachChild(childRouter) - verify(ribRefWatcher, never()).watchDeletedObject(any()) + verify(ribRefWatcher, never()).watchDeletedObject(any(), "") // Action: Detach the child interactor. router.detachChild(childRouter) // Verify: the reference watcher observes the detached interactor and child. - verify(ribRefWatcher).watchDeletedObject(eq(childInteractor)) + verify(ribRefWatcher).watchDeletedObject(eq(childInteractor), "") } @Test @@ -207,13 +207,13 @@ class InteractorAndRouterTest { } val rootRouter = TestRouterB(component, TestInteractorB(), ribRefWatcher) val child = addTwoNestedChildInteractors() - verify(ribRefWatcher, never()).watchDeletedObject(any()) + verify(ribRefWatcher, never()).watchDeletedObject(any(), "") // Action: Detach all child interactors. rootRouter.detachChild(child) // Verify: called four times. Twice for each interactor. - verify(ribRefWatcher, times(2)).watchDeletedObject(any()) + verify(ribRefWatcher, times(2)).watchDeletedObject(any(), "") } private fun addTwoNestedChildInteractors(): Router { diff --git a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt index b3c58fffc..fb4d8c70c 100644 --- a/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt +++ b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibRefWatcherTest.kt @@ -36,14 +36,14 @@ class RibRefWatcherTest { fun watchDeletedObject_whenObjectIsNull_shouldDoNothing() { ribRefWatcher.enableLeakCanary() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(null) + ribRefWatcher.watchDeletedObject(null, "") verifyNoInteractions(referenceWatcher) } @Test fun watchDeletedObject_whenReferenceWatcherIsNull_shouldDoNothing() { ribRefWatcher.enableLeakCanary() - ribRefWatcher.watchDeletedObject(Any()) + ribRefWatcher.watchDeletedObject(Any(), "") verifyNoInteractions(referenceWatcher) } @@ -52,30 +52,30 @@ class RibRefWatcherTest { ribRefWatcher.enableLeakCanary() val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher).watch(obj, "") } @Test fun watchDeletedObject_whenNonNullRefWithDisabledLeakCanary_shouldDoNothing() { val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher, never()).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher, never()).watch(obj, "") } @Test fun watchDeletedObject_whenObjectIsNullWithULeak_shouldDoNothing() { ribRefWatcher.enableULeakLifecycleTracking() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(null) + ribRefWatcher.watchDeletedObject(null, "") verifyNoInteractions(referenceWatcher) } @Test fun watchDeletedObject_whenReferenceWatcherIsNullULeakEnabled_shouldDoNothing() { ribRefWatcher.enableULeakLifecycleTracking() - ribRefWatcher.watchDeletedObject(Any()) + ribRefWatcher.watchDeletedObject(Any(), "") verifyNoInteractions(referenceWatcher) } @@ -84,15 +84,15 @@ class RibRefWatcherTest { ribRefWatcher.enableULeakLifecycleTracking() val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher).watch(obj, "") } @Test fun watchDeletedObject_whenNonNullRefULeakDisabled_shouldDoNothing() { val obj = Any() ribRefWatcher.setReferenceWatcher(referenceWatcher) - ribRefWatcher.watchDeletedObject(obj) - verify(referenceWatcher, never()).watch(obj) + ribRefWatcher.watchDeletedObject(obj, "") + verify(referenceWatcher, never()).watch(obj, "") } }