diff --git a/CHANGELOG.md b/CHANGELOG.md index a829938f3..df7c08494 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,24 @@ * [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 + +### 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 81f5be082..e83db3668 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? @@ -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.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/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/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/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt b/android/conventions/settings.gradle.kts similarity index 78% rename from android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt rename to android/conventions/settings.gradle.kts index 5a838c90b..5db6c3eb8 100644 --- a/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/main/workers/monitoring/BackendReporter.kt +++ b/android/conventions/settings.gradle.kts @@ -13,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.uber.rib.workers.root.main.workers.monitoring - -fun interface BackendReporter { - fun report(genericMessage: String) +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..fbfd26dec --- /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=all", + "-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..94202bde7 --- /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=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 new file mode 100644 index 000000000..4f7b90d01 --- /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=all") + } +} 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() + } +} diff --git a/android/demos/compose/build.gradle b/android/demos/compose/build.gradle index 943c8a327..0b7c75e54 100644 --- a/android/demos/compose/build.gradle +++ b/android/demos/compose/build.gradle @@ -1,59 +1,44 @@ -apply plugin: 'com.android.application' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "org.jetbrains.kotlin.kapt" +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 @@ -62,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/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/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/flipper/build.gradle b/android/demos/flipper/build.gradle index 5be99aee7..efe0727be 100644 --- a/android/demos/flipper/build.gradle +++ b/android/demos/flipper/build.gradle @@ -13,49 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' - 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' @@ -63,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/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/build.gradle b/android/demos/intellij/build.gradle index bf8a16d0f..52110171f 100644 --- a/android/demos/intellij/build.gradle +++ b/android/demos/intellij/build.gradle @@ -13,50 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' - 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/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/memory-leaks/build.gradle b/android/demos/memory-leaks/build.gradle index e822b4dc4..6a83737f1 100644 --- a/android/demos/memory-leaks/build.gradle +++ b/android/demos/memory-leaks/build.gradle @@ -13,66 +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 - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' -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/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/demos/rib-workers/build.gradle b/android/demos/rib-workers/build.gradle index de78a8a04..46eefe2cc 100644 --- a/android/demos/rib-workers/build.gradle +++ b/android/demos/rib-workers/build.gradle @@ -1,62 +1,48 @@ -apply plugin: 'com.android.application' -apply plugin: 'org.jetbrains.kotlin.android' -apply plugin: "org.jetbrains.kotlin.kapt" +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/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..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 @@ -23,6 +23,7 @@ import com.facebook.flipper.plugins.inspector.DescriptorMapping import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin import com.facebook.soloader.SoLoader import com.uber.rib.flipper.RibTreePlugin +import com.uber.rib.workers.root.logger.ApplicationLevelWorkerLogger class ComposeApplication : Application() { @@ -36,5 +37,7 @@ class ComposeApplication : Application() { client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults())) client.start() } + + ApplicationLevelWorkerLogger.start() } } 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..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,13 +16,8 @@ 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 -import com.uber.rib.workers.root.main.workers.monitoring.BackendReporter -import com.uber.rib.workers.root.main.workers.monitoring.RibWorkerMonitor @motif.Scope interface RootScope { @@ -38,17 +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 backendReporter(): BackendReporter = BackendReporter {} - - fun ribWorkerMonitor(backendReporter: BackendReporter): RibWorkerMonitor { - return RibWorkerMonitor(backendReporter) + fun view(parentViewGroup: ViewGroup): RootView { + return RootView(parentViewGroup.context) } } } 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..ba2d49f76 --- /dev/null +++ b/android/demos/rib-workers/src/main/kotlin/com/uber/rib/workers/root/logger/ApplicationLevelWorkerLogger.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.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.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 [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 = "WorkerLogger" + + private val workerTimeStampMap = ConcurrentHashMap() + + @OptIn(DelicateCoroutinesApi::class) + fun start() { + RibEvents.enableRibActionEmissions() + + GlobalScope.launch { + RibEvents.ribActionEvents + .filter { it.ribActionEmitterType == RibActionEmitterType.DEPRECATED_WORKER } + .asFlow() + .collect { it.logWorkerDuration() } + } + } + + private fun RibActionInfo.logWorkerDuration() { + if (ribActionState == RibActionState.STARTED && !ribActionEmitterName.isClassNameInMap()) { + workerTimeStampMap[this.ribActionEmitterName] = currentTimeMillis() + } else if ( + ribActionState == RibActionState.COMPLETED && ribActionEmitterName.isClassNameInMap() + ) { + val startedTimeStamp = workerTimeStampMap[ribActionEmitterName] + startedTimeStamp?.let { + val totalDuration = getTotalDuration(it) + this.logDuration(totalDuration) + } + workerTimeStampMap.remove(ribActionEmitterName) + } + } + + private fun String.isClassNameInMap(): Boolean = workerTimeStampMap.containsKey(this) + + private fun getTotalDuration(preOnStartDuration: Long): Long = + currentTimeMillis() - preOnStartDuration + + private fun RibActionInfo.logDuration(totalDuration: Long) { + Log.d( + LOG_TAG, + "${this.ribActionEmitterName} ${this.ribEventType} took $totalDuration ms on $originalCallerThreadName thread", + ) + } +} 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/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 d7d39f30d..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 @@ -15,10 +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.RibDispatchers +import com.uber.rib.core.RibEvents import com.uber.rib.core.WorkerBinder import com.uber.rib.core.asRibCoroutineWorker import com.uber.rib.core.asWorker @@ -47,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 { @@ -59,12 +63,15 @@ 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 ") - // Given uiWorker specifies its CoroutineContext, - // RibDispatchers.Main for Ui worker will remain besides Dispatchers.Default - WorkerBinder.bind(this, workers, RibDispatchers.Default) + 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) 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/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/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 diff --git a/android/gradle.properties b/android/gradle.properties index ca9f81400..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.14.3-SNAPSHOT +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/ 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/gradle/libs.versions.toml b/android/gradle/libs.versions.toml new file mode 100644 index 000000000..54b4c6753 --- /dev/null +++ b/android/gradle/libs.versions.toml @@ -0,0 +1,120 @@ +[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.6.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.7.3" +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.7.3" +ktfmt = "0.43" +ktlint = "0.48.2" +leakcanary = "2.10" +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" } +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" } +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..0c3d1754c --- /dev/null +++ b/android/gradle/test-libs.versions.toml @@ -0,0 +1,17 @@ +[versions] +compile-testing = "0.17" +coroutines = "1.7.3" +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" } diff --git a/android/libraries/rib-android-compose/build.gradle b/android/libraries/rib-android-compose/build.gradle index a9fefc752..c923b5c1d 100644 --- a/android/libraries/rib-android-compose/build.gradle +++ b/android/libraries/rib-android-compose/build.gradle @@ -13,47 +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" +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") -} - -kotlin { - explicitApi() + 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 45bb02a46..2e4cabff9 100644 --- a/android/libraries/rib-android-core/build.gradle +++ b/android/libraries/rib-android-core/build.gradle @@ -14,42 +14,19 @@ * 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.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 -} - -kotlin { - explicitApi() + implementation(libs.javax.inject) + implementation(libs.annotation) + implementation(libs.appcompat) + testImplementation(libs.appcompat) + testImplementation(testLibs.robolectric) } 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/build.gradle b/android/libraries/rib-android/build.gradle index a5b2275da..17f04c47e 100644 --- a/android/libraries/rib-android/build.gradle +++ b/android/libraries/rib-android/build.gradle @@ -13,50 +13,31 @@ * 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.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") -} - -kotlin { - explicitApi() + 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.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 3a0e3f7b8..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 @@ -17,12 +17,14 @@ package com.uber.rib.core -import android.R import android.content.Intent 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 @@ -91,7 +93,8 @@ abstract class RibActivity : @CallSuper override fun onCreate(savedInstanceState: android.os.Bundle?) { super.onCreate(savedInstanceState) - val rootViewGroup = findViewById(R.id.content) + initViewTreeOwners() + val rootViewGroup = findViewById(android.R.id.content) _lifecycleFlow.tryEmit(createOnCreateEvent(savedInstanceState)) val wrappedBundle: Bundle? = if (savedInstanceState != null) Bundle(savedInstanceState) else null @@ -99,7 +102,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) } } @@ -152,7 +155,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() @@ -233,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/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-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!", ) } } 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) diff --git a/android/libraries/rib-base/build.gradle b/android/libraries/rib-base/build.gradle index 8e9df1a21..fff35d78c 100644 --- a/android/libraries/rib-base/build.gradle +++ b/android/libraries/rib-base/build.gradle @@ -13,57 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.apt - } +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.kotlinKapt) + alias(libs.plugins.mavenPublish) } -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() - 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 } } - -kotlin { - explicitApi() -} 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..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 @@ -19,6 +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.triggerRibActionAndEmitEvents import com.uber.rib.core.lifecycle.InteractorEvent import io.reactivex.CompletableSource import io.reactivex.Observable @@ -36,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, RibActionEmitter { @Inject public lateinit var injectedPresenter: P internal var actualPresenter: P? = null private val _lifecycleFlow = MutableSharedFlow(1, 0, BufferOverflow.DROP_OLDEST) @@ -102,14 +103,49 @@ 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 { + triggerRibActionAndEmitEvents( + it, + RibActionEmitterType.PRESENTER, + RibEventType.ATTACHED, + ) { + it.dispatchLoad() + } + } + + triggerRibActionAndEmitEvents( + this, + RibActionEmitterType.INTERACTOR, + RibEventType.ATTACHED, + ) { + didBecomeActive(savedInstanceState) + } } public open fun dispatchDetach(): P { - (getPresenter() as? Presenter)?.dispatchUnload() - willResignActive() + val presenter = (getPresenter() as? Presenter) + presenter?.let { + triggerRibActionAndEmitEvents( + it, + RibActionEmitterType.PRESENTER, + RibEventType.DETACHED, + ) { + it.dispatchUnload() + } + } + + triggerRibActionAndEmitEvents( + this, + RibActionEmitterType.INTERACTOR, + RibEventType.DETACHED, + ) { + willResignActive() + } + _lifecycleFlow.tryEmit(InteractorEvent.INACTIVE) + return getPresenter() } 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..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 { +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 9ba03ddfb..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 @@ -43,12 +43,34 @@ 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 : RibActionEmitter { + /** Called when worker is started. Children coroutines can be launched in [scope]. */ 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) {} +} + +/** 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 ---- // @@ -106,6 +128,17 @@ public fun CoroutineScope.bind( return BindWorkerHandleImpl(bindJob, unbindJob) } +/** Binds [workers] 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. * @@ -192,7 +225,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) : 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..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 @@ -15,15 +15,37 @@ */ package com.uber.rib.core +import androidx.annotation.VisibleForTesting import io.reactivex.Observable 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 = mutableRouterEvents.asObservable() + + @JvmStatic + public val ribActionEvents: Observable = mutableRibDurationEvents.asObservable() + + /** Indicates if [ribActionEvents] will be emitting. */ + public var areRibActionEmissionsAllowed: Boolean = false + @VisibleForTesting internal set + + /** + * To be called before start observing/collecting on [ribActionEvents] (usually at your earliest + * application point) + */ + @JvmStatic + public fun enableRibActionEmissions() { + this.areRibActionEmissionsAllowed = true + } /** * @param eventType [RibEventType] @@ -31,13 +53,118 @@ 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)) + @JvmStatic + public fun emitRouterEvent(eventType: RibEventType, child: Router<*>, parent: Router<*>?) { + mutableRouterEvents.tryEmit(RibRouterEvent(eventType, child, parent)) } - public companion object { - private val instance: RibEvents = RibEvents() + /** + * Calls related RIB action (e.g. didBecomeActive) and emits emission of ATTACHED/DETACHED events + * for each [RibActionEmitter] (e.g. Concrete Interactor, Presenter, Router, 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( + ribActionEmitter: RibActionEmitter, + ribActionEmitterType: RibActionEmitterType, + ribEventType: RibEventType, + ribAction: () -> Unit, + ) { + emitRibEventActionIfNeeded( + ribActionEmitter, + ribActionEmitterType, + ribEventType, + RibActionState.STARTED, + ) + ribAction() + emitRibEventActionIfNeeded( + ribActionEmitter, + ribActionEmitterType, + ribEventType, + RibActionState.COMPLETED, + ) + } + + /** + * Emits ATTACHED/DETACHED events for each RIB component. + * + * @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: RIB action state (STARTED/COMPLETED). For example prior and after call + * of Interactor.didBecomeActive + */ + private fun emitRibEventActionIfNeeded( + ribActionEmitter: RibActionEmitter, + ribActionEmitterType: RibActionEmitterType, + ribEventType: RibEventType, + ribActionState: RibActionState, + ) { + if (!areRibActionEmissionsAllowed) { + // 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 + } - @JvmStatic public fun getInstance(): RibEvents = instance + val ribActionInfo = + RibActionInfo( + ribActionEmitter.javaClass.name, + ribActionEmitterType, + ribEventType, + ribActionState, + Thread.currentThread().name, + ) + mutableRibDurationEvents.tryEmit(ribActionInfo) } } + +/** Holds relevant RIB event information */ +public data class RibActionInfo( + /** Related RIB Action concrete class name */ + val ribActionEmitterName: String, + + /** The current RIB event type being bound (e.g. INTERACTOR/PRESENTER/ROUTER/WORKER) */ + val ribActionEmitterType: RibActionEmitterType, + + /** + * 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) */ + val ribActionState: RibActionState, + + /** Original caller thread where the RIB action happens */ + val originalCallerThreadName: String, +) + +/** + * Contract for all related Rib Action Types (Interactor, Presenter, Router, Worker) where will be + * emitting via [ribActionEvents] observable + */ +public interface RibActionEmitter + +public enum class RibActionEmitterType { + ROUTER, + PRESENTER, + INTERACTOR, + DEPRECATED_WORKER, +} + +/** 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/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/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..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 @@ -19,6 +19,7 @@ import android.os.Looper import androidx.annotation.CallSuper import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting +import com.uber.rib.core.RibEvents.triggerRibActionAndEmitEvents import java.util.Locale import java.util.concurrent.CopyOnWriteArrayList @@ -33,7 +34,7 @@ protected constructor( public open val interactor: I, private val ribRefWatcher: RibRefWatcher, private val mainThread: Thread, -) { +) : RibActionEmitter { private val children: MutableList> = CopyOnWriteArrayList() private val interactorGeneric: Interactor<*, *> get() = interactor as Interactor<*, *> @@ -113,13 +114,21 @@ protected constructor( ) } } - children.add(childRouter) + + triggerRibActionAndEmitEvents( + childRouter, + RibActionEmitterType.ROUTER, + RibEventType.ATTACHED, + ) { + children.add(childRouter) + } + 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) @@ -141,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, @@ -157,9 +169,17 @@ protected constructor( .handleNonFatalWarning("A RIB tried to detach a child that was never attached", null) } } - childRouter.dispatchDetach() + + triggerRibActionAndEmitEvents( + childRouter, + RibActionEmitterType.ROUTER, + RibEventType.DETACHED, + ) { + childRouter.dispatchDetach() + } + if (isChildRemoved) { - RibEvents.getInstance().emitEvent(RibEventType.DETACHED, childRouter, this) + RibEvents.emitRouterEvent(RibEventType.DETACHED, childRouter, this) } } 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..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 @@ -31,7 +31,14 @@ import kotlinx.coroutines.CoroutineDispatcher * RibDispatchers.Default) from [Worker] will take priority over the one passed via a * [WorkerBinder.bind] call */ -public interface Worker { +@Deprecated( + message = + """ + com.uber.rib.core.Worker is deprecated in favor of com.uber.rib.core.RibCoroutineWorker + """, + replaceWith = ReplaceWith("RibCoroutineWorker"), +) +public interface Worker : RibActionEmitter { /** * When overriden, will specify on which [CoroutineContext] the [Worker] will be bound via @@ -43,7 +50,6 @@ public interface Worker { * 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 @@ -52,8 +58,8 @@ public interface Worker { * * @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-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..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 @@ -18,15 +18,14 @@ 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.triggerRibActionAndEmitEvents 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 import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.DelicateCoroutinesApi @@ -44,21 +43,15 @@ 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("coroutineScope.bind(ribCoroutineWorker)"), +) 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 @@ -66,22 +59,16 @@ 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, - workerBinderListenerWeakRef, ) /** @@ -91,19 +78,30 @@ 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, + ) { + 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, dispatcherAtBinder) + bind(interactor, interactorWorker) } } @@ -113,23 +111,16 @@ 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, - workerBinderListenerWeakRef, ) /** @@ -139,18 +130,30 @@ 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, + ) { + 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, dispatcherAtBinder) + bind(presenter, worker) } } @@ -245,44 +248,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, @@ -298,9 +263,8 @@ private fun getJobCoroutineContext( private fun > Worker.bind( lifecycle: SharedFlow, lifecycleRange: ClosedRange, - dispatcherAtBinder: CoroutineDispatcher = RibDispatchers.Unconfined, - workerDurationListenerWeakRef: WeakReference?, ): WorkerUnbinder { + val dispatcherAtBinder = RibCoroutinesConfig.deprecatedWorkerDispatcher val coroutineContext = getJobCoroutineContext( dispatcherAtBinder, @@ -333,20 +297,21 @@ private fun > Worker.bind( lifecycle .takeWhile { it < lifecycleRange.endInclusive } .onCompletion { - bindAndReportWorkerInfo( - workerDurationListenerWeakRef, - WorkerEvent.STOP, - coroutineContext, + triggerRibActionAndEmitEvents( + this@bind, + RibActionEmitterType.DEPRECATED_WORKER, + RibEventType.DETACHED, ) { onStop() } + completable.onComplete() } .collect { - bindAndReportWorkerInfo( - workerDurationListenerWeakRef, - WorkerEvent.START, - coroutineContext, + triggerRibActionAndEmitEvents( + this@bind, + RibActionEmitterType.DEPRECATED_WORKER, + RibEventType.ATTACHED, ) { onStart(workerScopeProvider) } @@ -354,34 +319,3 @@ private fun > Worker.bind( } return WorkerUnbinder(job::cancel) } - -private inline fun Worker.bindAndReportWorkerInfo( - workerBinderListeners: WeakReference?, - event: WorkerEvent, - coroutineContext: CoroutineContext, - 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) -} 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() 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..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 @@ -16,9 +16,13 @@ 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 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 +40,7 @@ class InteractorAndRouterTest { private lateinit var interactor: TestInteractor private lateinit var router: TestRouter + private val ribActionInfoObserver = TestObserver() @Before fun setup() { @@ -45,26 +50,62 @@ class InteractorAndRouterTest { } interactor = TestInteractor(childInteractor) router = TestRouter(interactor, component) + RibEvents.enableRibActionEmissions() } @Test fun attach_shouldAttachChildController() { + // Given. + ribActionEvents.subscribe(ribActionInfoObserver) + // When. router.dispatchAttach(null) // Then. + val ribActionInfoValues = ribActionInfoObserver.values() + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibActionEmitterType.INTERACTOR, + RibActionState.COMPLETED, + "com.uber.rib.core.InteractorAndRouterTest${'$'}TestInteractor", + ) verify(childInteractor).dispatchAttach(null) } + @Test + fun attach_withoutAllowingEmissions_shouldNotEmtRibActionEvents() { + // Given. + RibEvents.areRibActionEmissionsAllowed = false + ribActionEvents.subscribe(ribActionInfoObserver) + + // When. + router.dispatchAttach(null) + + // Then. + assertThat(ribActionInfoObserver.values()).isEmpty() + } + @Test fun detach_shouldDetachChildController() { // Given. + ribActionEvents.subscribe(ribActionInfoObserver) router.dispatchAttach(null) // When. router.dispatchDetach() // Then. + val ribActionInfoValues = ribActionInfoObserver.values() + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.DETACHED, + RibActionEmitterType.ROUTER, + RibActionState.COMPLETED, + "com.uber.rib.core.FakeRouter", + ) verify(childInteractor).dispatchDetach() } @@ -146,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 @@ -166,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/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/RibEventsUtils.kt b/android/libraries/rib-base/src/test/kotlin/com/uber/rib/core/RibEventsUtils.kt new file mode 100644 index 000000000..2833c04d9 --- /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, + expectedRibActionEmitterType: RibActionEmitterType, + ribActionState: RibActionState, + ribClassName: String, + ) { + Truth.assertThat(this.ribEventType).isEqualTo(expectedRibEventType) + Truth.assertThat(this.ribActionEmitterType).isEqualTo(expectedRibActionEmitterType) + Truth.assertThat(this.ribActionState).isEqualTo(ribActionState) + Truth.assertThat(this.ribActionEmitterName).isEqualTo(ribClassName) + } +} 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, "") } } 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..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 @@ -17,6 +17,8 @@ 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 @@ -24,7 +26,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 kotlin.coroutines.CoroutineContext +import io.reactivex.observers.TestObserver import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle @@ -35,7 +37,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 @@ -54,14 +55,14 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { this } } - private val workerBinderListener: WorkerBinderListener = mock() private val fakeWorker = FakeWorker() private val interactor = FakeInteractor>() + private val ribActionInfoObserver = TestObserver() @Before fun setUp() { - WorkerBinder.initializeMonitoring(workerBinderListener) + RibEvents.enableRibActionEmissions() } @Test @@ -137,7 +138,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 } @@ -147,7 +148,7 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { } @Test - fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() { + fun bind_whenSubscribeToLifecycleInWorker_observerIsCalledEagerly() = runTest { val interactor = object : Interactor>() {} var enteredUnconfined = false val worker = Worker { @@ -179,48 +180,60 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { @Test fun bind_withUnconfinedCoroutineDispatchers_shouldReportBinderInformationForOnStart() = runTest { - val binderDurationCaptor = argumentCaptor() + ribActionEvents.subscribe(ribActionInfoObserver) bindFakeWorker() - verify(workerBinderListener).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.firstValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.START, - RibDispatchers.Unconfined, - ) + val ribActionInfoValues = ribActionInfoObserver.values() + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibActionEmitterType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.FakeWorker", + ) } @Test - fun bind_multipleWorkers_shouldReportBinderTwice() = runTest { + fun bind_withDisabledRibActionEvents_shouldNotEmitActionEvents() = runTest { + RibEvents.areRibActionEmissionsAllowed = false + ribActionEvents.subscribe(ribActionInfoObserver) + bindFakeWorker() + assertThat(ribActionInfoObserver.values()).isEmpty() + } + + @Test + fun bind_multipleWorkers_shouldReportBinderUiWorker() = runTest { + ribActionEvents.subscribe(ribActionInfoObserver) val uiWorker = UiWorker() - val binderDurationCaptor = 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, - ) + val ribActionInfoValues = ribActionInfoObserver.values() + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.ATTACHED, + RibActionEmitterType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.UiWorker", + ) } @Test fun unbind_withUnconfinedCoroutineDispatchers_shouldReportBinderDurationForOnStop() = runTest { - val binderDurationCaptor = argumentCaptor() + ribActionEvents.subscribe(ribActionInfoObserver) val unbinder = bindFakeWorker() unbinder.unbind() - verify(workerBinderListener, times(2)).onBindCompleted(binderDurationCaptor.capture()) - binderDurationCaptor.secondValue.assertWorkerDuration( - "FakeWorker", - WorkerEvent.STOP, - RibDispatchers.Unconfined, - ) + val ribActionInfoValues = ribActionInfoObserver.values() + ribActionInfoValues + .last() + .assertRibActionInfo( + RibEventType.DETACHED, + RibActionEmitterType.DEPRECATED_WORKER, + RibActionState.COMPLETED, + ribClassName = "com.uber.rib.core.FakeWorker", + ) } private fun bindFakeWorker(): WorkerUnbinder { @@ -233,16 +246,6 @@ class WorkerBinderTest(private val adaptFromRibCoroutineWorker: Boolean) { interactor.enableTestScopeOverride() } - private fun WorkerBinderInfo.assertWorkerDuration( - expectedWorkerClassName: String, - expectedWorkerEvent: WorkerEvent, - expectedCoroutineContext: CoroutineContext, - ) { - assertThat(workerName).contains(expectedWorkerClassName) - assertThat(workerEvent).isEqualTo(expectedWorkerEvent) - assertThat(expectedCoroutineContext).isEqualTo(expectedCoroutineContext) - } - companion object { @JvmStatic @Parameterized.Parameters(name = "adaptFromRibCoroutineWorker = {0}") diff --git a/android/libraries/rib-compiler-app/build.gradle b/android/libraries/rib-compiler-app/build.gradle index eab2d7ad0..6da04f79a 100644 --- a/android/libraries/rib-compiler-app/build.gradle +++ b/android/libraries/rib-compiler-app/build.gradle @@ -14,28 +14,23 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +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 - - testImplementation deps.test.compileTesting -} + compileOnly(libs.annotation) + compileOnly(libs.autoservice) + compileOnly(libs.android.api) -kotlin { - explicitApi() + 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 e32433c75..33eff4ef0 100644 --- a/android/libraries/rib-compiler-test/build.gradle +++ b/android/libraries/rib-compiler-test/build.gradle @@ -16,21 +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" +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-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-coroutines-test/README.md b/android/libraries/rib-coroutines-test/README.md index bd241274d..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.14.2' + implementation 'com.uber.rib:rib-coroutines-test:0.15.2' } ``` diff --git a/android/libraries/rib-coroutines-test/build.gradle b/android/libraries/rib-coroutines-test/build.gradle index 3ad9692d3..ae4e3b01a 100644 --- a/android/libraries/rib-coroutines-test/build.gradle +++ b/android/libraries/rib-coroutines-test/build.gradle @@ -14,33 +14,27 @@ * limitations under the License. */ - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) +} dependencies { - api project(':libraries:rib-coroutines') - api deps.kotlin.coroutinesTest - api deps.test.junit - - compileOnly deps.external.android - - 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 - -} + api(project(':libraries:rib-coroutines')) + api(testLibs.coroutines.test) + api(testLibs.junit) + + compileOnly(libs.android.api) + + 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) -kotlin { - explicitApi() } 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..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 @@ -15,7 +15,7 @@ */ package com.uber.rib.core -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.CoroutineDispatcher import org.junit.rules.TestWatcher import org.junit.runner.Description @@ -23,16 +23,19 @@ 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() { + 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!! } } 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() diff --git a/android/libraries/rib-coroutines/README.md b/android/libraries/rib-coroutines/README.md index 688659bd8..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.14.2' + implementation 'com.uber.rib:rib-coroutines:0.15.2' } ``` diff --git a/android/libraries/rib-coroutines/build.gradle b/android/libraries/rib-coroutines/build.gradle index 0e9d206f4..a1c457171 100644 --- a/android/libraries/rib-coroutines/build.gradle +++ b/android/libraries/rib-coroutines/build.gradle @@ -14,32 +14,26 @@ * limitations under the License. */ - -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) +} dependencies { - api deps.uber.autodisposeCoroutines - api deps.kotlin.coroutinesAndroid - api deps.kotlin.coroutinesRx2 - - compileOnly deps.external.android - - 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 -} - -kotlin { - explicitApi() + api(libs.autodispose.coroutines) + api(libs.coroutines.android) + api(libs.coroutines.rx2) + + compileOnly(libs.android.api) + + 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-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 } diff --git a/android/libraries/rib-debug-utils/build.gradle b/android/libraries/rib-debug-utils/build.gradle index e81da5a3a..f7f15783e 100644 --- a/android/libraries/rib-debug-utils/build.gradle +++ b/android/libraries/rib-debug-utils/build.gradle @@ -14,16 +14,16 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() - -dependencies { - implementation project(":libraries:rib-base") +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } kotlin { explicitApi() + jvmToolchain(11) +} + +dependencies { + implementation(project(":libraries:rib-base")) } diff --git a/android/libraries/rib-router-navigator/build.gradle b/android/libraries/rib-router-navigator/build.gradle index 78ff35956..2f2adca0d 100644 --- a/android/libraries/rib-router-navigator/build.gradle +++ b/android/libraries/rib-router-navigator/build.gradle @@ -14,26 +14,21 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() +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 - - testImplementation deps.test.junit - testImplementation deps.test.mockitoKotlin - testImplementation deps.test.truth -} + 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) -kotlin { - explicitApi() + testImplementation(testLibs.junit) + testImplementation(testLibs.mockitoKotlin) + testImplementation(testLibs.truth) } 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 } diff --git a/android/libraries/rib-screen-stack-base/build.gradle b/android/libraries/rib-screen-stack-base/build.gradle index c0e91d466..4641140e9 100644 --- a/android/libraries/rib-screen-stack-base/build.gradle +++ b/android/libraries/rib-screen-stack-base/build.gradle @@ -14,38 +14,21 @@ * 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.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 -} - -kotlin { - explicitApi() + 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 edeac509e..2b1cf8981 100644 --- a/android/libraries/rib-test/build.gradle +++ b/android/libraries/rib-test/build.gradle @@ -14,22 +14,17 @@ * limitations under the License. */ -apply plugin: "org.jetbrains.kotlin.jvm" -apply plugin: "com.vanniktech.maven.publish" - -sourceCompatibility = deps.build.javaVersion.toString() -targetCompatibility = deps.build.javaVersion.toString() - -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 +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.mavenPublish) } -kotlin { - explicitApi() +dependencies { + 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 06c1fe062..0e2aebb8f 100644 --- a/android/libraries/rib-workflow-test/build.gradle +++ b/android/libraries/rib-workflow-test/build.gradle @@ -13,27 +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" +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. @@ -44,13 +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 -} - -kotlin { - explicitApi() + 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-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/libraries/rib-workflow/build.gradle b/android/libraries/rib-workflow/build.gradle index 8f4c9a122..e0b5910c2 100644 --- a/android/libraries/rib-workflow/build.gradle +++ b/android/libraries/rib-workflow/build.gradle @@ -14,40 +14,23 @@ * 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.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 -} - -kotlin { - explicitApi() + 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 ae81184fd..687eee600 100644 --- a/android/tooling/rib-flipper-plugin/build.gradle +++ b/android/tooling/rib-flipper-plugin/build.gradle @@ -13,45 +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" +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-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/build.gradle b/android/tooling/rib-intellij-plugin/build.gradle index 5981a65fc..9975a418d 100644 --- a/android/tooling/rib-intellij-plugin/build.gradle +++ b/android/tooling/rib-intellij-plugin/build.gradle @@ -1,17 +1,14 @@ -buildscript { - dependencies { - classpath deps.build.intellijPlugin - } +plugins { + id("ribs.kotlin-library-conventions") + alias(libs.plugins.intellij) } -apply plugin: "java" -apply plugin: "org.jetbrains.intellij" -apply plugin: "org.jetbrains.kotlin.jvm" - group "com.uber.rib" repositories { mavenLocal() + google() + mavenCentral() } dependencies { @@ -32,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. @@ -59,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" @@ -76,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/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); 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, diff --git a/android/tooling/utils/intellij-broadcast-core/build.gradle b/android/tooling/utils/intellij-broadcast-core/build.gradle index efb6442ec..ddb95555d 100644 --- a/android/tooling/utils/intellij-broadcast-core/build.gradle +++ b/android/tooling/utils/intellij-broadcast-core/build.gradle @@ -14,43 +14,25 @@ * limitations under the License. */ -apply plugin: 'com.android.library' -apply plugin: "com.vanniktech.maven.publish" +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 2eae422aa..779515e6f 100644 --- a/android/tutorials/tutorial1/build.gradle +++ b/android/tutorials/tutorial1/build.gradle @@ -14,53 +14,25 @@ * limitations under the License. */ -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.nullaway - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") } -apply plugin: 'net.ltgt.errorprone' -apply plugin: 'com.android.application' -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.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 faaecc022..8da1d92e0 100644 --- a/android/tutorials/tutorial2/build.gradle +++ b/android/tutorials/tutorial2/build.gradle @@ -14,59 +14,29 @@ * limitations under the License. */ -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - classpath deps.build.gradlePlugins.errorprone - classpath deps.build.gradlePlugins.nullaway - } +plugins { + id("ribs.kotlin-android-application-errorprone-conventions") } -apply plugin: 'net.ltgt.errorprone' -apply plugin: 'com.android.application' -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 fef7cd446..e6fcb60bb 100644 --- a/android/tutorials/tutorial3-completed/build.gradle +++ b/android/tutorials/tutorial3-completed/build.gradle @@ -13,48 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' - 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 6f080bb75..7c98c5d3a 100644 --- a/android/tutorials/tutorial3/build.gradle +++ b/android/tutorials/tutorial3/build.gradle @@ -13,47 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' - 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 91da6b818..e5e6f4b4b 100644 --- a/android/tutorials/tutorial4/build.gradle +++ b/android/tutorials/tutorial4/build.gradle @@ -13,52 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -buildscript { - dependencies { - classpath deps.build.gradlePlugins.android - } +plugins { + id("ribs.kotlin-android-application-conventions") + alias(libs.plugins.kotlinKapt) } -apply plugin: 'com.android.application' - 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")) }