diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 38f93db3..6be6df65 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,5 +4,5 @@ ## Check list -- [ ] I have updated `whatsnew.md` if required. +- [ ] I have updated `CHANGELOG.md` if required. - [ ] I have updated documentation if required. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6850e6b..1c2752b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,29 +16,55 @@ jobs: - uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '11' + java-version: '17' - uses: gradle/wrapper-validation-action@v1 - - uses: gradle/gradle-build-action@v2 + - name: "Build project & run tests" + uses: gradle/gradle-build-action@v2 + with: + arguments: | + build + projectHealth + mergeLintSarif + mergeDetektSarif + :plugins:buildPlugins + --continue + - name: "Verify publishing" + uses: gradle/gradle-build-action@v2 with: - arguments: build + arguments: publishToMavenLocal + - uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: build/lint-merged.sarif + category: lint + - uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: build/detekt-merged.sarif + category: detekt - name: Upload failure artifacts if: failure() uses: actions/upload-artifact@v3 with: - name: unit-test-failures - path: '**/build/reports' + name: reports + path: | + **/build/reports/ + !**/build/reports/dependency-analysis/ instrumentation-tests: name: Instrumentation tests - runs-on: macOS-latest - timeout-minutes: 20 + runs-on: macos-13 + timeout-minutes: 30 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '11' + java-version: '17' - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/gradle-build-action@v2 + - name: Pre build sources before launching emulator + run: ./gradlew compileDebugAndroidTestSources - name: AVD cache uses: actions/cache@v3 id: avd-cache @@ -46,23 +72,23 @@ jobs: path: | ~/.android/avd/* ~/.android/adb* - key: avd-31 - - name: create AVD and generate snapshot for caching + key: avd-29 + - name: Create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 31 + # Use API 29 https://github.com/ReactiveCircus/android-emulator-runner/issues/222 + api-level: 29 arch: x86_64 force-avd-creation: false emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none disable-animations: false script: echo "Generated AVD snapshot for caching." - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - name: Instrumentation tests uses: reactivecircus/android-emulator-runner@v2 with: - api-level: 31 + # Use API 29 https://github.com/ReactiveCircus/android-emulator-runner/issues/222 + api-level: 29 arch: x86_64 force-avd-creation: false emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none @@ -72,9 +98,21 @@ jobs: ./gradlew connectedCheck - name: Upload failed instrumentation artifacts if: failure() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: - name: instrumentation-failures-31 + name: instrumentation-failures path: | **/build/reports logcat.out + + check-documentation: + name: Check documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: cp CHANGELOG.md documentation/changelog.md + - run: pip install mkdocs-material + - run: mkdocs build --strict diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 28829940..15e8b714 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -15,5 +15,6 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.x' + - run: cp CHANGELOG.md documentation/changelog.md - run: pip install mkdocs-material - run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index ae3f416a..1bf55bc3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ .idea /local.properties .DS_Store -/build +build /captures .externalNativeBuild diff --git a/documentation/whatsnew.md b/CHANGELOG.md similarity index 72% rename from documentation/whatsnew.md rename to CHANGELOG.md index 20237362..a4db014d 100644 --- a/documentation/whatsnew.md +++ b/CHANGELOG.md @@ -2,7 +2,56 @@ ### Pending changes -No pending changes +([#195](https://github.com/badoo/MVICore/pull/195)): +Updated Kotlin to 1.9.23 + +([#193](https://github.com/badoo/MVICore/pull/193)): +Updated Kotlin to 1.8.10 + +### 1.4.0 + +#### Additions + +([#179](https://github.com/badoo/MVICore/pull/179)): +Introduced the ability to specify observation scheduler within the `Binder` class (see the `observeOn` DSL below), as well as the `observeOn` infix operator for `Connection` class (related to `Binder`). + +An example of how you might use this is as follows: + +```kotlin +// mvicore-android example +testLifecycleOwner.lifecycle.createDestroy { + observeOn(mainScheduler) { + bind(events to uiConsumer1) + bind(events to uiConsumer2) + } + observeOn(backgroundScheduler) { + bind(events to backgroundConsumer1) + bind(events to backgroundConsumer2) + } + bind(events to uiConsumer3 observeOn mainScheduler) + bind(events to backgroundConsumer3 observeOn backgroundScheduler) +} + +// binder example +binder.observeOn(mainScheduler) { + bind(events to uiConsumer1) + bind(events to uiConsumer2) +} +``` + +See more details in [advanced binder](../binder/binder-advanced/#setting-connections-observation-scheduler) section. + +([#177](https://github.com/badoo/MVICore/pull/177)): +Updated AndroidX appcompat to 1.4.1 and lifecycle to 2.5.1. Also updated Compile and Target SDK to API 33. + +([#176](https://github.com/badoo/MVICore/pull/176)): +Updated RxJava to 2.2.21 and RxKotlin to 2.4.0 + +([#173](https://github.com/badoo/MVICore/pull/173)): +Updated Kotlin to 1.7.10 + +([#162](https://github.com/badoo/MVICore/pull/162)): +Updated Kotlin to 1.6.21 (the plan is to update to 1.7.x fairly soon) ### 1.3.1 diff --git a/binder/build.gradle b/binder/build.gradle deleted file mode 100644 index b421ccf3..00000000 --- a/binder/build.gradle +++ /dev/null @@ -1,55 +0,0 @@ -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -group = 'com.github.badoo.mvicore' - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - -repositories { - jcenter() -} - -dependencies { - def deps = rootProject.ext.deps - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') - testImplementation deps('org.mockito.kotlin:mockito-kotlin') -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -task packageJavadoc(type: Jar, dependsOn: javadoc) { - from javadoc.outputDirectory - classifier = 'javadoc' -} - -artifacts { - archives packageSources - archives packageJavadoc -} diff --git a/binder/build.gradle.kts b/binder/build.gradle.kts new file mode 100644 index 00000000..8fde34fc --- /dev/null +++ b/binder/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("java") + id("mvi-core-publish-java") + id("kotlin") + id("org.jetbrains.dokka") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType { + useJUnitPlatform() +} + +dependencies { + api(libs.rxjava2) + implementation(libs.rxkotlin) + implementation(libs.kotlin.stdlib) + + testRuntimeOnly(libs.junit5.engine) + testImplementation(libs.junit5.api) + testImplementation(libs.mockito.kotlin) +} diff --git a/binder/detekt-baseline.xml b/binder/detekt-baseline.xml new file mode 100644 index 00000000..5df9333d --- /dev/null +++ b/binder/detekt-baseline.xml @@ -0,0 +1,11 @@ + + + + + MaxLineLength:Connection.kt$infix + MaxLineLength:ManualLifecycle.kt$ManualLifecycle$class + ReturnCount:Consumer.kt$fun <In> Consumer<In>.wrapWithMiddleware( standalone: Boolean = true, name: String? = null, postfix: String? = null, wrapperOf: Any? = null ): Consumer<In> + TooManyFunctions:Binder.kt$Binder : Disposable + UseCheckOrError:StandaloneMiddleware.kt$StandaloneMiddleware$throw IllegalStateException("Middleware was initialised in standalone mode, can't accept other connections") + + diff --git a/binder/src/main/java/com/badoo/binder/Binder.kt b/binder/src/main/java/com/badoo/binder/Binder.kt index e98ce979..ca78539c 100644 --- a/binder/src/main/java/com/badoo/binder/Binder.kt +++ b/binder/src/main/java/com/badoo/binder/Binder.kt @@ -8,13 +8,14 @@ import com.badoo.binder.middleware.wrapWithMiddleware import io.reactivex.Observable import io.reactivex.Observable.wrap import io.reactivex.ObservableSource +import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.functions.Consumer import io.reactivex.rxkotlin.plusAssign class Binder( - private val lifecycle: Lifecycle? = null + private val lifecycle: Lifecycle? = null, ) : Disposable { private val disposables = CompositeDisposable() private val connections = mutableListOf, Middleware<*, *>?>>() @@ -31,7 +32,7 @@ class Binder( // region bind - fun bind(connection: Pair, Consumer>) { + fun bind(connection: Pair, Consumer>) { bind( Connection( from = connection.first, @@ -41,7 +42,7 @@ class Binder( ) } - fun bind(connection: Connection) { + fun bind(connection: Connection) { val consumer = connection.to val middleware = consumer.wrapWithMiddleware( standalone = false, @@ -85,12 +86,14 @@ class Binder( middleware.onBind(connection) this + .optionalObserveOn(connection.observeScheduler) .doOnNext { middleware.onElement(connection, it) } .doFinally { middleware.onComplete(connection) } .subscribe(middleware) } else { - subscribe(connection.to) + optionalObserveOn(connection.observeScheduler) + .subscribe(connection.to) } } @@ -139,7 +142,41 @@ class Binder( disposables.dispose() } + /** + * Any binds that occur within [BinderObserveOnScope] will be observed on the provided scheduler + */ + fun observeOn(scheduler: Scheduler, observeOnScopeFunc: BinderObserveOnScope.() -> Unit) { + BinderObserveOnScope(this, scheduler).apply(observeOnScopeFunc) + } + fun clear() { disposables.clear() } + + private fun Observable.optionalObserveOn(scheduler: Scheduler?) = + if (scheduler != null) { + observeOn(scheduler) + } else { + this + } + + class BinderObserveOnScope( + private val binder: Binder, + private val observeScheduler: Scheduler + ) { + fun bind(connection: Pair, Consumer>) { + binder.bind( + Connection( + from = connection.first, + to = connection.second, + connector = null, + observeScheduler = observeScheduler + ) + ) + } + + fun bind(connection: Connection) { + binder.bind(connection.observeOn(observeScheduler)) + } + } } diff --git a/binder/src/main/java/com/badoo/binder/Connection.kt b/binder/src/main/java/com/badoo/binder/Connection.kt index 8fa8b0ac..2d62f698 100644 --- a/binder/src/main/java/com/badoo/binder/Connection.kt +++ b/binder/src/main/java/com/badoo/binder/Connection.kt @@ -3,13 +3,15 @@ package com.badoo.binder import com.badoo.binder.connector.Connector import com.badoo.binder.connector.NotNullConnector import io.reactivex.ObservableSource +import io.reactivex.Scheduler import io.reactivex.functions.Consumer data class Connection( val from: ObservableSource? = null, val to: Consumer, val connector: Connector? = null, - val name: String? = null + val name: String? = null, + val observeScheduler: Scheduler? = null, ) { companion object { private const val ANONYMOUS: String = "anonymous" @@ -39,7 +41,19 @@ infix fun Pair, Consumer>.named(name: String): name = name ) +infix fun Pair, Consumer>.observeOn(scheduler: Scheduler): Connection = + Connection( + from = first, + to = second, + observeScheduler = scheduler + ) + infix fun Connection.named(name: String) = copy( name = name ) + +infix fun Connection.observeOn(scheduler: Scheduler) = + copy( + observeScheduler = scheduler + ) diff --git a/binder/src/test/java/com/badoo/binder/BinderTest.kt b/binder/src/test/java/com/badoo/binder/BinderTest.kt index d90a1d25..d6369b87 100644 --- a/binder/src/test/java/com/badoo/binder/BinderTest.kt +++ b/binder/src/test/java/com/badoo/binder/BinderTest.kt @@ -4,7 +4,7 @@ import com.badoo.binder.connector.Connector import io.reactivex.Observable import io.reactivex.ObservableSource import io.reactivex.subjects.PublishSubject -import org.junit.Test +import org.junit.jupiter.api.Test class BinderTest { diff --git a/binder/src/test/java/com/badoo/binder/LifecycleTest.kt b/binder/src/test/java/com/badoo/binder/LifecycleTest.kt index 6bcbb501..60b6c414 100644 --- a/binder/src/test/java/com/badoo/binder/LifecycleTest.kt +++ b/binder/src/test/java/com/badoo/binder/LifecycleTest.kt @@ -5,13 +5,12 @@ import com.badoo.binder.lifecycle.ManualLifecycle import io.reactivex.ObservableSource import io.reactivex.functions.Consumer import io.reactivex.subjects.PublishSubject -import org.junit.Test +import org.junit.jupiter.api.Test import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify - class LifecycleTest { private val lifecycle: ManualLifecycle = Lifecycle.manual() diff --git a/binder/src/test/java/com/badoo/binder/TestConsumer.kt b/binder/src/test/java/com/badoo/binder/TestConsumer.kt index b5fad7d7..4d295e89 100644 --- a/binder/src/test/java/com/badoo/binder/TestConsumer.kt +++ b/binder/src/test/java/com/badoo/binder/TestConsumer.kt @@ -1,7 +1,7 @@ package com.badoo.binder import io.reactivex.functions.Consumer -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals class TestConsumer : Consumer { val values = mutableListOf() diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 2179f99a..00000000 --- a/build.gradle +++ /dev/null @@ -1,124 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext.kotlinVersion = '1.3.72' - - repositories { - google() - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.4.0" - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -ext { - // Android - androidLifecycleVersion = '2.2.0' - androidAppCompatVersion = '1.3.1' - androidMaterialVersion = '1.2.1' - constraintLayoutVersion = '2.0.2' - - // Rx - rxJavaVersion = '2.2.10' - rxKotlinVersion = '2.2.0' - rxAndroidVersion = '2.1.1' - - // DI - daggerVersion = '2.29.1' - - // Utils - debugDrawerVersion = '0.9.0' - timberVersion = '4.7.1' - scalpelVersion = '1.1.2' - glideVersion = '4.11.0' - - // Network - okhttpVersion = '3.10.0' - retrofitVersion = '2.4.0' - gsonVersion = '2.8.5' - - // Testing - junitVersion = '4.12' - supportTestVersion = '1.3.0' - mockitoKotlinVersion = '4.0.0' - - depsWithVersion = [ - // Kotlin - "org.jetbrains.kotlin:kotlin-stdlib-jdk7" : kotlinVersion, - - // Android - "androidx.lifecycle:lifecycle-common-java8" : androidLifecycleVersion, - "androidx.appcompat:appcompat" : androidAppCompatVersion, - "androidx.constraintlayout:constraintlayout" : constraintLayoutVersion, - "com.google.android.material:material" : androidMaterialVersion, - - // Rx - "io.reactivex.rxjava2:rxjava" : rxJavaVersion, - "io.reactivex.rxjava2:rxkotlin" : rxKotlinVersion, - "io.reactivex.rxjava2:rxandroid" : rxAndroidVersion, - - // DI - "com.google.dagger:dagger" : daggerVersion, - "com.google.dagger:dagger-android" : daggerVersion, - "com.google.dagger:dagger-android-support" : daggerVersion, - "com.google.dagger:dagger-compiler" : daggerVersion, - "com.google.dagger:dagger-android-processor" : daggerVersion, - - // DebugDrawer - "com.github.lenguyenthanh.debugdrawer:debugdrawer" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-base" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-view" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-no-op" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-view-no-op" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-commons" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-actions" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-scalpel" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-timber" : debugDrawerVersion, - "com.github.lenguyenthanh.debugdrawer:debugdrawer-network-quality": debugDrawerVersion, - - // Utils - "com.jakewharton.timber:timber" : timberVersion, - "com.jakewharton.scalpel:scalpel" : scalpelVersion, - "com.github.bumptech.glide:glide" : glideVersion, - "com.github.bumptech.glide:compiler" : glideVersion, - - // Network - "com.squareup.okhttp3:okhttp" : okhttpVersion, - "com.squareup.retrofit2:retrofit" : retrofitVersion, - "com.squareup.retrofit2:adapter-rxjava2" : retrofitVersion, - "com.squareup.retrofit2:converter-simplexml" : retrofitVersion, - "com.google.code.gson:gson" : gsonVersion, - - // Testing - "junit:junit" : junitVersion, - "org.jetbrains.kotlin:kotlin-test-junit" : kotlinVersion, - "org.mockito.kotlin:mockito-kotlin" : mockitoKotlinVersion, - "androidx.test:runner" : supportTestVersion, - "androidx.test:orchestrator" : supportTestVersion, - "androidx.test:rules" : supportTestVersion, - ] - - deps = { String key -> - def version = depsWithVersion[key] - if (version == null) throw new IllegalDependencyNotation("Version for library '$key' unknown. Update root build.gradle") - "$key:${version}" - } -} - -allprojects { - repositories { - google() - jcenter() - maven { url 'https://jitpack.io' } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..30db89c0 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,67 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath(libs.plugin.android) + classpath(libs.plugin.kotlin) + classpath("org.jetbrains.dokka:dokka-gradle-plugin:${libs.versions.dokka.get()}") + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +plugins { + id("mvi-core-collect-sarif") + id("com.autonomousapps.dependency-analysis") version libs.versions.dependencyAnalysis.get() + id("com.google.dagger.hilt.android") version(libs.versions.daggerVersion.get()) apply false +} + +dependencyAnalysis { + issues { + all { + onIncorrectConfiguration { + severity("fail") + } + onUnusedDependencies { + severity("fail") + } + } + project(":mvicore-demo:mvicore-demo-app") { + onUnusedDependencies { + severity("fail") + exclude("com.jakewharton.scalpel:scalpel") // Accessed using reflection + + // AGP 8.0.2 seems to insist that an app module has these even if there are not tests + exclude("junit:junit") + exclude("androidx.test:runner") + } + } + project(":mvicore-android") { + onUnusedDependencies { + severity("fail") + exclude("androidx.test:runner") // Accessed using reflection + } + } + } +} + +tasks.register("clean", Delete::class) { + delete(rootProject.buildDir) +} + +subprojects { + afterEvaluate { + tasks.withType(KotlinJvmCompile::class.java).configureEach { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + } +} diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 00000000..bb509cda --- /dev/null +++ b/detekt.yml @@ -0,0 +1,14 @@ +config: + warningsAsErrors: true + +style: + ForbiddenComment: + allowedPatterns: 'https://github.com/badoo/MVICore/issues/*' + MagicNumber: + excludes: [ '**/test/**', '**/*Test.kt' ] + MaxLineLength: + excludes: [ '**/test/**', '**/*.Test.kt', '**/*.Spec.kt' ] + excludeCommentStatements: true + WildcardImport: + active: true + excludeImports: [ ] diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 00000000..1a13c363 --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1 @@ +changelog.md diff --git a/documentation/binder/binder-advanced.md b/documentation/binder/binder-advanced.md index 9a278fe4..687329bc 100644 --- a/documentation/binder/binder-advanced.md +++ b/documentation/binder/binder-advanced.md @@ -37,7 +37,7 @@ binder.bind(output to input using OutputToInput) ## Naming connections -And you can optionally give names to any connection: +You can optionally give names to any connection: ```kotlin binder.bind(input to output named "MyConnection") // or @@ -49,3 +49,20 @@ Naming a connection signals that it's important to you. This will make more sens - You'll see connections with their respective names in the time-travel debug menu - You'll see connection names in logs if you use LoggingMiddleware - You can opt to dynamically add `Middlewares` only to named connections (if that's what you want) + +## Setting connections observation scheduler + +You can optionally set the observation scheduler for any connection: +```kotlin +binder.bind(input to output observeOn scheduler) +``` + +You can also use `Binder.observeOn` to reduce repetition: +```kotlin +binder.observeOn(scheduler) { + bind(input1 to output1) + bind(input2 to output2) +} +``` + +Specifying an observation scheduler ensures that the output is called on the specified scheduler. diff --git a/gradle.properties b/gradle.properties index 6dd0218e..ac56394d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,9 @@ # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..c2a88248 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,96 @@ +[versions] +kotlinVersion = "1.9.23" +detekt = "1.22.0" +dependencyAnalysis = "1.31.0" +dokka = "1.9.20" # Dokka versions no longer match Kotlin 1:1 + +# Android +androidLifecycleVersion = "2.6.1" +androidAppCompatVersion = "1.4.1" +androidMaterialVersion = "1.4.0" +constraintLayoutVersion = "2.1.0" + +# Rx +rxJavaVersion = "2.2.21" +rxKotlinVersion = "2.4.0" +rxAndroidVersion = "2.1.1" + +# DI +daggerVersion = "2.46.1" + +# Utils +debugDrawerVersion = "0.9.0" +timberVersion = "5.0.1" +scalpelVersion = "1.1.2" +glideVersion = "4.13.2" + +# Testing +retrofitVersion = "2.4.0" +gsonVersion = "2.10.1" + +# Testing +junit5 = "5.9.2" +supportTestVersion = "1.3.0" +mockitoKotlinVersion = "4.1.0" + +[libraries] +# Kotlin +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlinVersion" } + +# Android +androidx-arch-core-runtime = "androidx.arch.core:core-runtime:2.1.0" +androidx-lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "androidLifecycleVersion" } +androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "androidLifecycleVersion" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayoutVersion" } +google-material = { module = "com.google.android.material:material", version.ref = "androidMaterialVersion" } + +# Rx +rxjava2 = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxJavaVersion" } +rxkotlin = { module = "io.reactivex.rxjava2:rxkotlin", version.ref = "rxKotlinVersion" } +rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxAndroidVersion" } + +# DI +dagger-runtime = { module = "com.google.dagger:dagger", version.ref = "daggerVersion" } +dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "daggerVersion" } +hilt-runtime = { module = "com.google.dagger:hilt-android", version.ref = "daggerVersion" } +hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "daggerVersion" } + +# DebugDrawer +debugdrawer-impl = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer", version.ref = "debugDrawerVersion" } +debugdrawer-base = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-base", version.ref = "debugDrawerVersion" } +debugdrawer-view-impl = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-view", version.ref = "debugDrawerVersion" } +debugdrawer-noop = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-no-op", version.ref = "debugDrawerVersion" } +debugdrawer-view-noop = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-view-no-op", version.ref = "debugDrawerVersion" } +debugdrawer-commons = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-commons", version.ref = "debugDrawerVersion" } +debugdrawer-scalpel = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-scalpel", version.ref = "debugDrawerVersion" } +debugdrawer-timber = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-timber", version.ref = "debugDrawerVersion" } +debugdrawer-networkQuality = { module = "com.github.lenguyenthanh.debugdrawer:debugdrawer-network-quality", version.ref = "debugDrawerVersion" } + +# Utils +timber = { module = "com.jakewharton.timber:timber", version.ref = "timberVersion" } +scalpel = { module = "com.jakewharton.scalpel:scalpel", version.ref = "scalpelVersion" } +glide-runtime = { module = "com.github.bumptech.glide:glide", version.ref = "glideVersion" } +glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glideVersion" } + +# Network +retrofit-runtime = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofitVersion" } +retrofit-adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "retrofitVersion" } +retrofit-converter-simplexml = { module = "com.squareup.retrofit2:converter-simplexml", version.ref = "retrofitVersion" } +gson = { module = "com.google.code.gson:gson", version.ref = "gsonVersion" } + +# Testing +junit4 = "junit:junit:4.13.2" +junit5-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit5" } +junit5-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } +junit5-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "junit5"} +mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlinVersion" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "supportTestVersion" } +hamcrest-core = "org.hamcrest:hamcrest-core:1.3" + +plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" } +plugin-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } +plugin-android = "com.android.tools.build:gradle:8.2.2" + +[plugins] +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c0..c1962a79 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 78fef57e..509c4a29 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jul 12 00:03:54 BST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip diff --git a/gradlew b/gradlew index 4f906e0c..aeb74cbb 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,98 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +137,109 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9..6689b85b 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..7a157e09 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,8 @@ +jdk: + - openjdk17 +before_install: + - sdk install java 17.0.1-open + - sdk use java 17.0.1-open +install: + - echo "Building artifacts" + - ./gradlew clean publishToMavenLocal diff --git a/mkdocs.yml b/mkdocs.yml index 78c0f9f1..a538dda8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -44,7 +44,7 @@ docs_dir: documentation nav: - Overview: index.md -- What's new: whatsnew.md +- Changelog: changelog.md - Features: - Core concepts: features/coreconcepts.md - Your first and simplest feature: features/reducerfeature.md diff --git a/mvicore-android/build.gradle b/mvicore-android/build.gradle deleted file mode 100644 index 5eb3104e..00000000 --- a/mvicore-android/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'org.jetbrains.dokka' -apply plugin: 'com.github.dcendents.android-maven' - -group='com.github.badoo.mvicore' - -android { - compileSdkVersion 30 - defaultConfig { - minSdkVersion 15 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -dependencies { - def deps = rootProject.ext.deps - - implementation deps('androidx.appcompat:appcompat') - implementation deps('androidx.lifecycle:lifecycle-common-java8') - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - implementation deps('io.reactivex.rxjava2:rxandroid') - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') - androidTestImplementation deps("androidx.test:runner") - androidTestImplementation deps("androidx.test:rules") - androidTestImplementation deps("androidx.test:rules") - - implementation project(":mvicore") -} - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - -repositories { - jcenter() -} - -sourceSets { - main { - java {} - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives packageSources -} diff --git a/mvicore-android/build.gradle.kts b/mvicore-android/build.gradle.kts new file mode 100644 index 00000000..6b91e3cc --- /dev/null +++ b/mvicore-android/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("org.jetbrains.dokka") + id("mvi-core-publish-android") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +group = "com.github.badoo.mvicore" + +android { + namespace = "com.badoo.mvicore.android" + compileSdk = 34 + + defaultConfig { + minSdk = 15 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + testOptions { + unitTests.all { + it.useJUnitPlatform() + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + api(project(":mvicore")) + api(project(":binder")) + api(libs.androidx.lifecycle.common) + api(libs.rxjava2) + + implementation(libs.kotlin.stdlib) + implementation(libs.rxandroid) + + testRuntimeOnly(libs.junit5.engine) + testImplementation(libs.junit5.api) + testImplementation(libs.junit5.params) + testImplementation(libs.androidx.arch.core.runtime) + testImplementation(libs.androidx.lifecycle.runtime) + + androidTestImplementation(libs.junit4) + androidTestImplementation(libs.androidx.test.runner) + androidTestImplementation(libs.hamcrest.core) +} diff --git a/mvicore-android/detekt-baseline.xml b/mvicore-android/detekt-baseline.xml new file mode 100644 index 00000000..6aaf7cce --- /dev/null +++ b/mvicore-android/detekt-baseline.xml @@ -0,0 +1,7 @@ + + + + + MaxLineLength:AndroidTimeCapsule.kt$AndroidTimeCapsule$fun saveState(outState: Bundle) + + diff --git a/mvicore-android/lint-baseline.xml b/mvicore-android/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-android/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-android/src/main/AndroidManifest.xml b/mvicore-android/src/main/AndroidManifest.xml deleted file mode 100644 index 4f1f6122..00000000 --- a/mvicore-android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt new file mode 100644 index 00000000..d5090c83 --- /dev/null +++ b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/LifecycleExtensionsTest.kt @@ -0,0 +1,270 @@ +package com.badoo.mvicore.android.lifecycle + +import androidx.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +import androidx.lifecycle.Lifecycle +import com.badoo.binder.Binder +import com.badoo.binder.observeOn +import io.reactivex.functions.Consumer +import io.reactivex.internal.schedulers.RxThreadFactory +import io.reactivex.plugins.RxJavaPlugins +import io.reactivex.subjects.PublishSubject +import java.util.concurrent.CountDownLatch +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +typealias LifecycleEvent = Lifecycle.(Binder.() -> Unit) -> Unit + +class LifecycleExtensionsTest { + private val subject = PublishSubject.create() + private val consumerTester = ConsumerTester() + private val testLifecycleOwner = TestLifecycleOwner() + + private val mainScheduler = RxJavaPlugins + .createSingleScheduler(RxThreadFactory("main", Thread.NORM_PRIORITY, false)) + .apply { start() } + + private val backgroundScheduler = RxJavaPlugins + .createSingleScheduler(RxThreadFactory("background", Thread.NORM_PRIORITY, false)) + .apply { start() } + + @BeforeEach + fun setup() { + ArchTaskExecutor.getInstance() + .setDelegate(object : TaskExecutor() { + override fun executeOnDiskIO(runnable: Runnable) = runnable.run() + override fun postToMainThread(runnable: Runnable) = runnable.run() + override fun isMainThread(): Boolean = true + }) + } + + @AfterEach + fun teardown() { + ArchTaskExecutor.getInstance().setDelegate(null) + } + + @ParameterizedTest(name = "GIVEN lifecycle event {0} AND lifecycle states {1} THEN event consumption should be {2}") + @MethodSource("generateTestData") + fun `GIVEN lifecycle event AND lifecycle state THEN event consumption should be handled correctly`( + lifecycleEvent: LifecycleEvent, + lifecycleStates: List, + eventShouldBeConsumed: Boolean + ) { + testLifecycleOwner.lifecycle.lifecycleEvent { + bind(subject to consumerTester) + } + + lifecycleStates.forEach { testLifecycleOwner.state = it } + + subject.onNext(Unit) + + if (eventShouldBeConsumed) { + consumerTester.verifyInvoked() + } else { + consumerTester.verifyNotInvoked() + } + assertEquals(eventShouldBeConsumed, subject.hasObservers()) + } + + @Test + fun `GIVEN initial lifecycle is created AND createDestroy with observe on schedulers dsl WHEN event emitted THEN verify correct thread called`() { + val testThreadName = Thread.currentThread().name + + val subject = PublishSubject.create() + val mainThreadConsumerTester = ConsumerTester() + val backgroundThreadConsumerTester = ConsumerTester() + val unconfinedThreadConsumerTester = ConsumerTester() + + val countDownLatch = CountDownLatch(3) + + testLifecycleOwner.lifecycle.createDestroy { + observeOn(mainScheduler) { + bind(subject to Consumer { + mainThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + }) + } + observeOn(backgroundScheduler) { + bind(subject to Consumer { + backgroundThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + }) + } + bind(subject to Consumer { + unconfinedThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + }) + } + testLifecycleOwner.state = Lifecycle.State.CREATED + + subject.onNext(Unit) + + countDownLatch.await() + + mainThreadConsumerTester.verifyThreadName("main") + backgroundThreadConsumerTester.verifyThreadName("background") + unconfinedThreadConsumerTester.verifyThreadName(testThreadName) + } + + @Test + fun `GIVEN initial lifecycle is created AND createDestroy with schedulers infix WHEN event emitted THEN verify correct thread called`() { + val testThreadName = Thread.currentThread().name + + val subject = PublishSubject.create() + val mainThreadConsumerTester = ConsumerTester() + val backgroundThreadConsumerTester = ConsumerTester() + val unconfinedThreadConsumerTester = ConsumerTester() + + val countDownLatch = CountDownLatch(3) + + testLifecycleOwner.lifecycle.createDestroy { + bind(subject to Consumer { + mainThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + } observeOn mainScheduler) + bind(subject to Consumer { + backgroundThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + } observeOn backgroundScheduler) + bind(subject to Consumer { + unconfinedThreadConsumerTester.accept(Unit) + countDownLatch.countDown() + }) + } + testLifecycleOwner.state = Lifecycle.State.CREATED + + subject.onNext(Unit) + + countDownLatch.await() + + mainThreadConsumerTester.verifyThreadName("main") + backgroundThreadConsumerTester.verifyThreadName("background") + unconfinedThreadConsumerTester.verifyThreadName(testThreadName) + } + + companion object { + @JvmStatic + @Suppress("LongMethod") + fun generateTestData(): List { + return listOf( + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::createDestroy, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::startStop, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = emptyList(), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.CREATED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.STARTED), + eventShouldBeConsumed = false + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf(Lifecycle.State.RESUMED), + eventShouldBeConsumed = true + ), + TestData( + lifecycleEvent = Lifecycle::resumePause, + lifecycleStates = listOf( + Lifecycle.State.CREATED, + Lifecycle.State.DESTROYED + ), + eventShouldBeConsumed = false + ) + ).map { Arguments.of(it.lifecycleEvent, it.lifecycleStates, it.eventShouldBeConsumed) } + } + } + + data class TestData( + val lifecycleEvent: LifecycleEvent, + val lifecycleStates: List, + val eventShouldBeConsumed: Boolean + ) + + private class ConsumerTester : Consumer { + private var wasCalled: Boolean = false + lateinit var threadName: String + + override fun accept(t: Unit?) { + wasCalled = true + threadName = Thread.currentThread().name + } + + fun verifyInvoked() { + assertEquals(true, wasCalled) + } + + fun verifyNotInvoked() { + assertEquals(false, wasCalled) + } + + fun verifyThreadName(name: String) { + assertEquals(true, threadName.startsWith(name)) + } + } +} diff --git a/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/TestLifecycleOwner.kt b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/TestLifecycleOwner.kt new file mode 100644 index 00000000..79def7c4 --- /dev/null +++ b/mvicore-android/src/test/java/com/badoo/mvicore/android/lifecycle/TestLifecycleOwner.kt @@ -0,0 +1,18 @@ +package com.badoo.mvicore.android.lifecycle + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry + +class TestLifecycleOwner : LifecycleOwner { + private val registry = LifecycleRegistry(this) + + var state: Lifecycle.State + get() = registry.currentState + set(value) { + registry.currentState = value + } + + override val lifecycle: Lifecycle + get() = registry +} diff --git a/mvicore-debugdrawer/build.gradle b/mvicore-debugdrawer/build.gradle deleted file mode 100644 index 29108e12..00000000 --- a/mvicore-debugdrawer/build.gradle +++ /dev/null @@ -1,80 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'org.jetbrains.dokka' -apply plugin: 'com.github.dcendents.android-maven' - -group='com.github.badoo.mvicore' - -android { - compileSdkVersion 30 - defaultConfig { - minSdkVersion 19 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -dependencies { - def deps = rootProject.ext.deps - - implementation deps('androidx.constraintlayout:constraintlayout') - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-base') - debugImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer') - debugImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-view') - releaseImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-no-op') - releaseImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-view-no-op') - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') - - implementation project(':mvicore') -} - -buildscript { - repositories { - jcenter() - maven { url 'https://jitpack.io' } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - -sourceSets { - main { - java {} - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives packageSources -} diff --git a/mvicore-debugdrawer/build.gradle.kts b/mvicore-debugdrawer/build.gradle.kts new file mode 100644 index 00000000..d7846b5a --- /dev/null +++ b/mvicore-debugdrawer/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("org.jetbrains.dokka") + id("mvi-core-publish-android") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +group = "com.github.badoo.mvicore" + +android { + namespace = "com.badoo.mvicore.debugdrawer" + compileSdk = 34 + defaultConfig { + minSdk = 19 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + api(project(":mvicore")) + api(libs.debugdrawer.base) + debugApi(libs.debugdrawer.impl) + releaseApi(libs.debugdrawer.noop) + + implementation(libs.androidx.constraintlayout) + implementation(libs.rxjava2) + implementation(libs.kotlin.stdlib) + + debugImplementation(libs.debugdrawer.view.impl) + + releaseImplementation(libs.debugdrawer.view.noop) +} diff --git a/mvicore-debugdrawer/lint-baseline.xml b/mvicore-debugdrawer/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-debugdrawer/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-debugdrawer/lint.xml b/mvicore-debugdrawer/lint.xml new file mode 100644 index 00000000..7a6cda5f --- /dev/null +++ b/mvicore-debugdrawer/lint.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/mvicore-debugdrawer/src/main/AndroidManifest.xml b/mvicore-debugdrawer/src/main/AndroidManifest.xml deleted file mode 100644 index 009796c7..00000000 --- a/mvicore-debugdrawer/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/mvicore-debugdrawer/src/main/java/com/badoo/mvicore/debugdrawer/MviCoreControlsModule.kt b/mvicore-debugdrawer/src/main/java/com/badoo/mvicore/debugdrawer/MviCoreControlsModule.kt index 45bde643..7ff0dbcb 100644 --- a/mvicore-debugdrawer/src/main/java/com/badoo/mvicore/debugdrawer/MviCoreControlsModule.kt +++ b/mvicore-debugdrawer/src/main/java/com/badoo/mvicore/debugdrawer/MviCoreControlsModule.kt @@ -10,7 +10,10 @@ import android.widget.ImageButton import android.widget.Spinner import android.widget.Toast import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware -import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.PlaybackState.* +import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.PlaybackState.FINISHED_PLAYBACK +import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.PlaybackState.IDLE +import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.PlaybackState.PLAYBACK +import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.PlaybackState.RECORDING import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore.RecordKey import io.palaima.debugdrawer.DebugDrawer import io.palaima.debugdrawer.base.DebugModuleAdapter diff --git a/mvicore-debugdrawer/src/main/res/layout/list_item_simple_small.xml b/mvicore-debugdrawer/src/main/res/layout/list_item_simple_small.xml index e89c809d..a34ec9e0 100644 --- a/mvicore-debugdrawer/src/main/res/layout/list_item_simple_small.xml +++ b/mvicore-debugdrawer/src/main/res/layout/list_item_simple_small.xml @@ -1,5 +1,6 @@ + android:minHeight="?android:attr/listPreferredItemHeightSmall" + tools:ignore="SmallSp" /> diff --git a/mvicore-debugdrawer/src/main/res/values/colors.xml b/mvicore-debugdrawer/src/main/res/values/colors.xml index ec6f9344..1d2544bc 100755 --- a/mvicore-debugdrawer/src/main/res/values/colors.xml +++ b/mvicore-debugdrawer/src/main/res/values/colors.xml @@ -1,13 +1,6 @@ - #fafafa - #f5f5f5 #eeeeee #e0e0e0 #bdbdbd - #9e9e9e - #757575 - #616161 - #424242 - #212121 diff --git a/mvicore-demo/mvicore-demo-app/build.gradle b/mvicore-demo/mvicore-demo-app/build.gradle deleted file mode 100644 index 289dbba1..00000000 --- a/mvicore-demo/mvicore-demo-app/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-kapt' - -android { - compileSdkVersion 30 - defaultConfig { - applicationId "com.badoo.mvicoredemo" - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -dependencies { - // MVICore - implementation project(":mvicore") - implementation project(":mvicore-android") - implementation project(":mvicore-debugdrawer") - implementation project(':mvicore-demo:mvicore-demo-feature1') - implementation project(':mvicore-demo:mvicore-demo-feature2') - implementation project(':mvicore-plugin:middleware') - - // Kotlin - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - // Android - implementation deps('androidx.appcompat:appcompat') - implementation deps('androidx.constraintlayout:constraintlayout') - implementation deps('com.google.android.material:material') - - // Rx - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxandroid') - - // DI - implementation deps('com.google.dagger:dagger') - implementation(deps('com.google.dagger:dagger-android'), { exclude group: 'com.google.code.findbugs' }) - implementation(deps('com.google.dagger:dagger-android-support'), { exclude group: 'com.google.code.findbugs' }) - kapt deps('com.google.dagger:dagger-compiler') - kapt deps('com.google.dagger:dagger-android-processor') - - // DebugDrawer - debugImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer') - debugImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-view') - releaseImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-no-op') - releaseImplementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-view-no-op') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-commons') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-actions') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-scalpel') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-base') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-timber') - implementation deps('com.github.lenguyenthanh.debugdrawer:debugdrawer-network-quality') - - // Utils - implementation deps('com.jakewharton.timber:timber') - implementation deps('com.jakewharton.scalpel:scalpel') - implementation deps('com.squareup.okhttp3:okhttp') - implementation deps('com.github.bumptech.glide:glide') - kapt deps('com.github.bumptech.glide:compiler') -} diff --git a/mvicore-demo/mvicore-demo-app/build.gradle.kts b/mvicore-demo/mvicore-demo-app/build.gradle.kts new file mode 100644 index 00000000..834ed027 --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/build.gradle.kts @@ -0,0 +1,87 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("kotlin-kapt") + id("dagger.hilt.android.plugin") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +android { + namespace = "com.badoo.mvicoredemo" + compileSdk = 34 + defaultConfig { + applicationId = "com.badoo.mvicoredemo" + minSdk = 21 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + buildFeatures { + viewBinding = true + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + // MVICore + implementation(project(":mvicore")) + implementation(project(":mvicore-android")) + implementation(project(":mvicore-debugdrawer")) + implementation(project(":mvicore-demo:mvicore-demo-feature1")) + implementation(project(":mvicore-demo:mvicore-demo-feature2")) + implementation(project(":mvicore-plugin:middleware")) + implementation(project(":binder")) + + // Kotlin + implementation(libs.kotlin.stdlib) + + // Android + implementation(libs.androidx.appcompat) + implementation(libs.androidx.constraintlayout) + implementation(libs.google.material) + + // Rx + implementation(libs.rxjava2) + implementation(libs.rxandroid) + + // DI + implementation(libs.dagger.runtime) + implementation(libs.hilt.runtime) + kapt(libs.dagger.compiler) + kapt(libs.hilt.compiler) + + // DebugDrawer + debugImplementation(libs.debugdrawer.impl) + releaseImplementation(libs.debugdrawer.noop) + implementation(libs.debugdrawer.commons) + implementation(libs.debugdrawer.scalpel) + implementation(libs.debugdrawer.base) + implementation(libs.debugdrawer.timber) + implementation(libs.debugdrawer.networkQuality) + + // Utils + implementation(libs.timber) + implementation(libs.scalpel) + implementation(libs.glide.runtime) + kapt(libs.glide.compiler) + + androidTestImplementation(libs.junit4) + androidTestImplementation(libs.androidx.test.runner) +} diff --git a/mvicore-demo/mvicore-demo-app/detekt-baseline.xml b/mvicore-demo/mvicore-demo-app/detekt-baseline.xml new file mode 100644 index 00000000..d84cdd25 --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/detekt-baseline.xml @@ -0,0 +1,13 @@ + + + + + MagicNumber:ViewModelTransformer.kt$ViewModelTransformer$3 + MagicNumber:ViewModelTransformer1.kt$ViewModelTransformer1$3 + MaxLineLength:MainActivityBindings.kt$MainActivityBindings$binder.bind(combineLatest(feature1, feature2) to view using ViewModelTransformer() named "MainActivity.ViewModels") + TooManyFunctions:LifecycleDemoActivity.kt$LifecycleDemoActivity : AppCompatActivity + UtilityClassWithPublicConstructor:Auth.kt$PrefsHelper + WildcardImport:UiEventTransformer1.kt$import com.badoo.feature1.Feature1.Wish.* + WildcardImport:UiEventTransformer2.kt$import com.badoo.feature2.Feature2.Wish.* + + diff --git a/mvicore-demo/mvicore-demo-app/lint-baseline.xml b/mvicore-demo/mvicore-demo-app/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-demo/mvicore-demo-app/lint.xml b/mvicore-demo/mvicore-demo-app/lint.xml new file mode 100644 index 00000000..c53ed8ee --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/lint.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/mvicore-demo/mvicore-demo-app/src/main/AndroidManifest.xml b/mvicore-demo/mvicore-demo-app/src/main/AndroidManifest.xml index a0e3c812..0d35dc4c 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/AndroidManifest.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -8,6 +7,7 @@ + + android:name=".ui.launcher.LauncherActivity" + android:exported="true"> @@ -29,15 +30,18 @@ + android:theme="@style/AppTheme" + android:exported="false" /> + android:theme="@style/AppTheme.NoActionBar" + android:exported="false" /> + android:theme="@style/AppTheme.NoActionBar" + android:exported="false" /> diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/App.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/App.kt index c84b9482..9e080d60 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/App.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/App.kt @@ -1,6 +1,5 @@ package com.badoo.mvicoredemo -import android.annotation.SuppressLint import android.app.Application import com.badoo.binder.middleware.config.MiddlewareConfiguration import com.badoo.binder.middleware.config.Middlewares @@ -10,36 +9,24 @@ import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore import com.badoo.mvicore.middleware.DefaultPluginStore import com.badoo.mvicore.middleware.IdeaPluginMiddleware -import com.badoo.mvicoredemo.di.appscope.component.AppScopedComponent +import dagger.hilt.android.HiltAndroidApp import io.palaima.debugdrawer.timber.data.LumberYard import timber.log.Timber import javax.inject.Inject - +@HiltAndroidApp class App : Application() { @Inject lateinit var recordStore: RecordStore private val defaultStore = DefaultPluginStore(BuildConfig.APPLICATION_ID) - companion object { - @SuppressLint("StaticFieldLeak") - lateinit var component: AppScopedComponent - private set - } - override fun onCreate() { super.onCreate() - dagger() middlewares() timber() } - private fun dagger() { - component = AppScopedComponent(this) - component.get().inject(this) - } - private fun middlewares() { Middlewares.configurations.add( MiddlewareConfiguration( diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/auth/Auth.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/auth/Auth.kt index b7f1c2d2..275abcfa 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/auth/Auth.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/auth/Auth.kt @@ -5,14 +5,14 @@ import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionScopedComponent +import com.badoo.mvicoredemo.di.usersessionscope.UserManager import com.badoo.mvicoredemo.ui.login.LoginActivity import com.badoo.mvicoredemo.ui.main.MainActivity - fun AppCompatActivity.login() { storeIsLoggedIn(true) - UserSessionScopedComponent.get() + UserManager.getUserManager(application).userLoggedIn() + ContextCompat.startActivity( this, Intent(this, MainActivity::class.java).apply { @@ -24,7 +24,8 @@ fun AppCompatActivity.login() { fun AppCompatActivity.logout() { storeIsLoggedIn(false) - UserSessionScopedComponent.destroy() + UserManager.getUserManager(application).logout() + ContextCompat.startActivity( this, Intent(this, LoginActivity::class.java).apply { diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/ScopedComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/ScopedComponent.kt deleted file mode 100644 index 6cbf1e50..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/ScopedComponent.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.badoo.mvicoredemo.di - -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.disposables.Disposable -import java.lang.ref.WeakReference - -abstract class ScopedComponent { - - protected var component: T? = null - private set - - private val subScopes: MutableSet>> = mutableSetOf() - - private val disposables = CompositeDisposable() - - protected abstract fun create(): T - - protected open fun T.disposables(): Array = emptyArray() - - fun initialize() { - get() - } - - fun get(): T { - if (component == null) { - component = create().apply { disposables().forEach { disposables.add(it) } } - } - - return component!! - } - - fun dependAndGet(subScope: ScopedComponent<*>): T? { - subScopes.add(WeakReference(subScope)) - return get() - } - - open fun destroy() { - component = null - subScopes.forEach { it.get()?.destroy() } - subScopes.clear() - disposables.clear() - } -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/module/ActivityModule.java b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/module/ActivityModule.java deleted file mode 100755 index 9f34a94a..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/module/ActivityModule.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.badoo.mvicoredemo.di.activityscope.module; - -import android.app.Activity; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentManager; - -import dagger.Module; -import dagger.Provides; - -@Module -public class ActivityModule { - private final AppCompatActivity activity; - - public ActivityModule(AppCompatActivity activity) { - this.activity = activity; - } - - @Provides - Activity provideActivity() { - return activity; - } - - @Provides - AppCompatActivity provideAppCompatActivity() { - return activity; - } - - @Provides - FragmentManager provideFragmentManager() { - return activity.getSupportFragmentManager(); - } -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/scope/ActivityScope.java b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/scope/ActivityScope.java deleted file mode 100755 index 88c79eb1..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/activityscope/scope/ActivityScope.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.badoo.mvicoredemo.di.activityscope.scope; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface ActivityScope { -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppComponent.kt deleted file mode 100644 index c648003a..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppComponent.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.badoo.mvicoredemo.di.appscope.component - -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Resources -import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore -import com.badoo.mvicore.debugdrawer.MviCoreControlsModule -import com.badoo.mvicoredemo.App -import com.badoo.mvicoredemo.di.appscope.module.AndroidModule -import com.badoo.mvicoredemo.di.appscope.module.MviCoreModule -import com.badoo.mvicoredemo.di.appscope.scope.AppScope -import dagger.Component - - -@AppScope -@Component( - modules = [ - AndroidModule::class, - MviCoreModule::class - ] -) -interface AppComponent { - // expose AndroidModule - fun provideApp(): App - fun provideContext(): Context - fun provideResources(): Resources - fun provideSharedPreferences(): SharedPreferences - - // expose MviCoreModule -// fun logger(): Logger - fun recordStore(): RecordStore - fun debugDrawerControls(): MviCoreControlsModule - - fun inject(app: App) -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppScopedComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppScopedComponent.kt deleted file mode 100644 index f7f92b89..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/component/AppScopedComponent.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.badoo.mvicoredemo.di.appscope.component - -import android.content.Context -import com.badoo.mvicoredemo.di.ScopedComponent -import com.badoo.mvicoredemo.di.appscope.module.AndroidModule - -class AppScopedComponent( - private val context: Context -) : ScopedComponent() { - - override fun create(): AppComponent = - DaggerAppComponent.builder() - .androidModule(AndroidModule(context)) - .build() -} - diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/AndroidModule.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/AndroidModule.kt deleted file mode 100644 index 8631349f..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/AndroidModule.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.badoo.mvicoredemo.di.appscope.module - -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Resources - -import com.badoo.mvicoredemo.App - -import dagger.Module -import dagger.Provides - -@Module -class AndroidModule(context: Context) { - private val context: Context = context.applicationContext - - @Provides - fun provideApp(): App { - return context as App - } - - @Provides - fun provideContext(): Context { - return context - } - - @Provides - fun provideResources(): Resources { - return context.resources - } - - @Provides - fun provideSharedPreferences(): SharedPreferences { - return context.getSharedPreferences(App::class.java.name, Context.MODE_PRIVATE) - } -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/MviCoreModule.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/MviCoreModule.kt index 46075c11..28b3ab20 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/MviCoreModule.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/module/MviCoreModule.kt @@ -3,17 +3,20 @@ package com.badoo.mvicoredemo.di.appscope.module import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware.RecordStore import com.badoo.mvicore.consumer.playback.MemoryRecordStore import com.badoo.mvicore.debugdrawer.MviCoreControlsModule -import com.badoo.mvicoredemo.di.appscope.scope.AppScope import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import io.reactivex.android.schedulers.AndroidSchedulers import timber.log.Timber +import javax.inject.Singleton @Module +@InstallIn(SingletonComponent::class) class MviCoreModule { @Provides - @AppScope + @Singleton fun recordStore(): RecordStore = MemoryRecordStore( playbackScheduler = AndroidSchedulers.mainThread(), diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/scope/AppScope.java b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/scope/AppScope.java deleted file mode 100755 index d7a36d1e..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/appscope/scope/AppScope.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.badoo.mvicoredemo.di.appscope.scope; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface AppScope { -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManager.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManager.kt new file mode 100644 index 00000000..ab6bd20c --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManager.kt @@ -0,0 +1,44 @@ +package com.badoo.mvicoredemo.di.usersessionscope; + +import android.app.Application +import com.badoo.mvicoredemo.di.usersessionscope.component.UserComponentEntryPoint +import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionComponent +import dagger.hilt.EntryPoints +import io.reactivex.disposables.CompositeDisposable +import javax.inject.Inject +import javax.inject.Provider +import javax.inject.Singleton + +@Singleton +class UserManager @Inject constructor( + private val userSessionComponentBuilderProvider: Provider +) { + var userComponent: UserSessionComponent? = null + private set + + fun userLoggedIn() { + userComponent = userSessionComponentBuilderProvider.get().build() + } + + fun logout() { + userComponent?.apply { + val userPartsEntryPoint = UserComponentEntryPoint.get(this) + CompositeDisposable() + .apply { + addAll( + userPartsEntryPoint.feature1(), + userPartsEntryPoint.feature2(), + ) + } + .dispose() + } + userComponent = null + } + + companion object { + fun getUserManager(application: Application): UserManager = + EntryPoints + .get(application, UserManagerEntryPoint::class.java) + .userManager() + } +} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManagerEntryPoint.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManagerEntryPoint.kt new file mode 100644 index 00000000..75bc0ff8 --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/UserManagerEntryPoint.kt @@ -0,0 +1,11 @@ +package com.badoo.mvicoredemo.di.usersessionscope + +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@InstallIn(SingletonComponent::class) +@EntryPoint +interface UserManagerEntryPoint { + fun userManager(): UserManager +} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserComponentEntryPoint.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserComponentEntryPoint.kt new file mode 100644 index 00000000..116b8600 --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserComponentEntryPoint.kt @@ -0,0 +1,24 @@ +package com.badoo.mvicoredemo.di.usersessionscope.component + +import android.app.Application +import com.badoo.feature1.Feature1 +import com.badoo.feature2.Feature2 +import com.badoo.mvicoredemo.di.usersessionscope.UserManager +import dagger.hilt.EntryPoint +import dagger.hilt.EntryPoints +import dagger.hilt.InstallIn + +@InstallIn(UserSessionComponent::class) +@EntryPoint +interface UserComponentEntryPoint { + fun feature1(): Feature1 + fun feature2(): Feature2 + + companion object { + fun get(application: Application): UserComponentEntryPoint = + get(UserManager.getUserManager(application).userComponent!!) + + fun get(userComponent: UserSessionComponent): UserComponentEntryPoint = + EntryPoints.get(userComponent, UserComponentEntryPoint::class.java) + } +} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionComponent.kt index 984e3b61..06bc83b3 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionComponent.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionComponent.kt @@ -1,38 +1,13 @@ package com.badoo.mvicoredemo.di.usersessionscope.component -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Resources -import com.badoo.feature1.Feature1 -import com.badoo.feature2.Feature2 -import com.badoo.mvicore.consumer.middleware.PlaybackMiddleware -import com.badoo.mvicore.debugdrawer.MviCoreControlsModule -import com.badoo.mvicoredemo.App -import com.badoo.mvicoredemo.di.appscope.component.AppComponent -import com.badoo.mvicoredemo.di.usersessionscope.module.FeatureModule -import com.badoo.mvicoredemo.di.usersessionscope.scope.UserSessionScope -import dagger.Component - +import dagger.hilt.DefineComponent +import dagger.hilt.components.SingletonComponent @UserSessionScope -@Component( - dependencies = [ - AppComponent::class - ], - modules = [ - FeatureModule::class - ] -) +@DefineComponent(parent = SingletonComponent::class) interface UserSessionComponent { - // expose AppComponent - fun provideApp(): App - fun provideContext(): Context - fun provideResources(): Resources - fun provideSharedPreferences(): SharedPreferences - fun recordStore(): PlaybackMiddleware.RecordStore - fun debugDrawerControls(): MviCoreControlsModule - - // expose FeatureModule - fun feature1(): Feature1 - fun feature2(): Feature2 + @DefineComponent.Builder + interface Builder { + fun build(): UserSessionComponent + } } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScope.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScope.kt new file mode 100644 index 00000000..3b1369c6 --- /dev/null +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScope.kt @@ -0,0 +1,7 @@ +package com.badoo.mvicoredemo.di.usersessionscope.component + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.RUNTIME) +annotation class UserSessionScope diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScopedComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScopedComponent.kt deleted file mode 100644 index 9c2f6430..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/component/UserSessionScopedComponent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.badoo.mvicoredemo.di.usersessionscope.component - -import com.badoo.mvicoredemo.di.ScopedComponent -import com.badoo.mvicoredemo.App -import com.badoo.mvicoredemo.di.usersessionscope.module.FeatureModule -import io.reactivex.disposables.Disposable - -object UserSessionScopedComponent : ScopedComponent() { - - override fun create(): UserSessionComponent = - DaggerUserSessionComponent - .builder() - .appComponent(App.component.dependAndGet(this)) - .featureModule(FeatureModule()) - .build() - - override fun UserSessionComponent.disposables(): Array = arrayOf( - feature1(), - feature2() - ) -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/module/FeatureModule.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/module/FeatureModule.kt index 971f363c..80897f81 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/module/FeatureModule.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/module/FeatureModule.kt @@ -2,13 +2,15 @@ package com.badoo.mvicoredemo.di.usersessionscope.module import com.badoo.feature1.Feature1 import com.badoo.feature2.Feature2 -import com.badoo.mvicoredemo.di.usersessionscope.scope.UserSessionScope +import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionComponent +import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionScope import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn @Module +@InstallIn(UserSessionComponent::class) class FeatureModule { - @Provides @UserSessionScope fun feature1(): Feature1 = diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/scope/UserSessionScope.java b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/scope/UserSessionScope.java deleted file mode 100755 index 48035681..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/di/usersessionscope/scope/UserSessionScope.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.badoo.mvicoredemo.di.usersessionscope.scope; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import javax.inject.Scope; - -@Scope -@Retention(RetentionPolicy.RUNTIME) -public @interface UserSessionScope { -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/common/ObservableSourceActivity.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/common/ObservableSourceActivity.kt index 9ee2fcb6..b232d53e 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/common/ObservableSourceActivity.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/common/ObservableSourceActivity.kt @@ -4,7 +4,7 @@ import io.reactivex.ObservableSource import io.reactivex.Observer import io.reactivex.subjects.PublishSubject -abstract class ObservableSourceActivity : DebugActivity(), ObservableSource { +abstract class ObservableSourceActivity : DebugActivity(), ObservableSource { private val source = PublishSubject.create() diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/lifecycle/LifecycleDemoActivity.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/lifecycle/LifecycleDemoActivity.kt index c4ae8ecd..bf2972c4 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/lifecycle/LifecycleDemoActivity.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/lifecycle/LifecycleDemoActivity.kt @@ -1,7 +1,6 @@ package com.badoo.mvicoredemo.ui.lifecycle import android.os.Bundle -import android.util.Log import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.core.view.GravityCompat @@ -10,24 +9,24 @@ import com.badoo.binder.named import com.badoo.mvicore.android.lifecycle.CreateDestroyBinderLifecycle import com.badoo.mvicore.android.lifecycle.ResumePauseBinderLifecycle import com.badoo.mvicore.android.lifecycle.StartStopBinderLifecycle -import com.badoo.mvicoredemo.R +import com.badoo.mvicoredemo.databinding.ActivityLifecycleDemoBinding import init import io.reactivex.functions.Consumer import io.reactivex.subjects.PublishSubject -import kotlinx.android.synthetic.main.activity_main.drawerLayout -import kotlinx.android.synthetic.main.activity_main.navigationView -import kotlinx.android.synthetic.main.activity_main.toolbar +import timber.log.Timber class LifecycleDemoActivity : AppCompatActivity() { private val events = PublishSubject.create() private val dummyConsumer = Consumer { - Log.d("LifecycleDemo", it) + Timber.tag("LifecycleDemo").d(it) } + private lateinit var binding: ActivityLifecycleDemoBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_lifecycle_demo) + binding = ActivityLifecycleDemoBinding.inflate(layoutInflater) + setContentView(binding.root) Binder(CreateDestroyBinderLifecycle(lifecycle)) .bind(events to dummyConsumer named "Lifecycle#CreateDestroy") @@ -43,15 +42,15 @@ class LifecycleDemoActivity : AppCompatActivity() { } private fun setupDrawer() { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) - navigationView.init(drawerLayout, 1) + binding.navigationView.init(binding.drawerLayout, 1) } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { - drawerLayout.openDrawer(GravityCompat.START) + binding.drawerLayout.openDrawer(GravityCompat.START) true } else -> super.onOptionsItemSelected(item) diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/login/LoginActivity.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/login/LoginActivity.kt index 2ecd2dc8..0f71b2ef 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/login/LoginActivity.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/login/LoginActivity.kt @@ -1,10 +1,10 @@ package com.badoo.mvicoredemo.ui.login import android.os.Bundle +import android.view.View import androidx.appcompat.app.AppCompatActivity import com.badoo.mvicoredemo.R import com.badoo.mvicoredemo.auth.login -import kotlinx.android.synthetic.main.activity_login.signIn class LoginActivity : AppCompatActivity() { @@ -12,6 +12,6 @@ class LoginActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) - signIn.setOnClickListener { login() } + findViewById(R.id.signIn).setOnClickListener { login() } } } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivity.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivity.kt index f1f7ba2a..0edb100a 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivity.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivity.kt @@ -4,84 +4,78 @@ import android.os.Bundle import android.view.MenuItem import android.view.View import androidx.core.view.GravityCompat -import com.badoo.mvicoredemo.R import com.badoo.mvicoredemo.auth.logout +import com.badoo.mvicoredemo.databinding.ActivityMainBinding import com.badoo.mvicoredemo.glide.GlideApp import com.badoo.mvicoredemo.ui.common.ObservableSourceActivity import com.badoo.mvicoredemo.ui.main.analytics.FakeAnalyticsTracker -import com.badoo.mvicoredemo.ui.main.di.component.MainScreenInjector import com.badoo.mvicoredemo.ui.main.event.UiEvent import com.badoo.mvicoredemo.ui.main.event.UiEvent.ButtonClicked import com.badoo.mvicoredemo.ui.main.event.UiEvent.ImageClicked import com.badoo.mvicoredemo.ui.main.event.UiEvent.PlusClicked import com.badoo.mvicoredemo.ui.main.viewmodel.ViewModel +import dagger.hilt.android.AndroidEntryPoint import init import io.reactivex.functions.Consumer -import kotlinx.android.synthetic.main.activity_main.button0 -import kotlinx.android.synthetic.main.activity_main.button1 -import kotlinx.android.synthetic.main.activity_main.button2 -import kotlinx.android.synthetic.main.activity_main.button3 -import kotlinx.android.synthetic.main.activity_main.counter -import kotlinx.android.synthetic.main.activity_main.drawerLayout -import kotlinx.android.synthetic.main.activity_main.fab -import kotlinx.android.synthetic.main.activity_main.help -import kotlinx.android.synthetic.main.activity_main.image -import kotlinx.android.synthetic.main.activity_main.imageProgress -import kotlinx.android.synthetic.main.activity_main.navigationView -import kotlinx.android.synthetic.main.activity_main.showToasts -import kotlinx.android.synthetic.main.activity_main.signOut -import kotlinx.android.synthetic.main.activity_main.toolbar import javax.inject.Inject +@AndroidEntryPoint class MainActivity : ObservableSourceActivity(), Consumer { - @Inject lateinit var bindings: MainActivityBindings - @Inject lateinit var analyticsTracker: FakeAnalyticsTracker + @Inject + lateinit var bindings: MainActivityBindings + + @Inject + lateinit var analyticsTracker: FakeAnalyticsTracker private lateinit var buttons: List + private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - MainScreenInjector.get(this).inject(this) - setContentView(R.layout.activity_main) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) setupViews() setupDebugDrawer() bindings.setup(this) } private fun setupViews() { - buttons = listOf(button0, button1, button2, button3) + buttons = listOf(binding.button0, binding.button1, binding.button2, binding.button3) buttons.forEachIndexed { idx, button -> button.setOnClickListener { onNext(ButtonClicked(idx)) } } - image.setOnClickListener { onNext(ImageClicked) } - fab.setOnClickListener { onNext(PlusClicked) } - signOut.setOnClickListener { logout() } - showToasts.setOnClickListener { + binding.image.setOnClickListener { onNext(ImageClicked) } + binding.fab.setOnClickListener { onNext(PlusClicked) } + binding.signOut.setOnClickListener { logout() } + binding.showToasts.setOnClickListener { // Only for debugging purposes, otherwise should be part of the state! analyticsTracker.showToasts = !analyticsTracker.showToasts - showToasts.toggle(analyticsTracker.showToasts) + binding.showToasts.toggle(analyticsTracker.showToasts) } - help.setOnClickListener { + binding.help.setOnClickListener { HelpDialogFragment().show(supportFragmentManager, "help") } - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) - navigationView.init(drawerLayout, 0) + binding.navigationView.init(binding.drawerLayout, 0) } override fun accept(vm: ViewModel) { - counter.text = vm.counter.toString() - buttons.forEachIndexed { idx, button -> button.setBackgroundColor(resources.getColor(vm.buttonColors[idx]))} - imageProgress.visibility = if (vm.imageIsLoading) View.VISIBLE else View.GONE + binding.counter.text = vm.counter.toString() + buttons.forEachIndexed { idx, button -> + button.setBackgroundColor(resources.getColor(vm.buttonColors[idx])) + } + binding.imageProgress.visibility = if (vm.imageIsLoading) View.VISIBLE else View.GONE loadImage(vm.imageUrl) } override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { android.R.id.home -> { - drawerLayout.openDrawer(GravityCompat.START) + binding.drawerLayout.openDrawer(GravityCompat.START) true } + else -> super.onOptionsItemSelected(item) } @@ -90,7 +84,7 @@ class MainActivity : ObservableSourceActivity(), Consumer { GlideApp.with(this) .load(url) .centerCrop() - .into(image) + .into(binding.image) } } } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivityBindings.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivityBindings.kt index abb1183c..321fb828 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivityBindings.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/MainActivityBindings.kt @@ -4,7 +4,7 @@ import com.badoo.binder.named import com.badoo.binder.using import com.badoo.feature1.Feature1 import com.badoo.feature2.Feature2 -import com.badoo.mvicore.android.AndroidBindings +import com.badoo.mvicore.android.lifecycle.createDestroy import com.badoo.mvicoredemo.ui.main.analytics.FakeAnalyticsTracker import com.badoo.mvicoredemo.ui.main.event.UiEventTransformer1 import com.badoo.mvicoredemo.ui.main.event.UiEventTransformer2 @@ -13,18 +13,23 @@ import com.badoo.mvicoredemo.ui.main.viewmodel.ViewModelTransformer import com.badoo.mvicoredemo.utils.combineLatest class MainActivityBindings( - view: MainActivity, private val feature1: Feature1, private val feature2: Feature2, private val analyticsTracker: FakeAnalyticsTracker, private val newsListener: NewsListener -) : AndroidBindings(view) { - - override fun setup(view: MainActivity) { - binder.bind(combineLatest(feature1, feature2) to view using ViewModelTransformer() named "MainActivity.ViewModels") - binder.bind(view to feature1 using UiEventTransformer1()) - binder.bind(view to feature2 using UiEventTransformer2()) - binder.bind(view to analyticsTracker named "MainActivity.Analytics") - binder.bind(feature2.news to newsListener named "MainActivity.News") +) { + fun setup(view: MainActivity) { + view.lifecycle.createDestroy { + bind( + combineLatest( + feature1, + feature2 + ) to view using ViewModelTransformer() named "MainActivity.ViewModels" + ) + bind(view to feature1 using UiEventTransformer1()) + bind(view to feature2 using UiEventTransformer2()) + bind(view to analyticsTracker named "MainActivity.Analytics") + bind(feature2.news to newsListener named "MainActivity.News") + } } } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/analytics/FakeAnalyticsTracker.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/analytics/FakeAnalyticsTracker.kt index 8f344df5..1690a5c8 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/analytics/FakeAnalyticsTracker.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/analytics/FakeAnalyticsTracker.kt @@ -13,7 +13,11 @@ class FakeAnalyticsTracker( override fun accept(uiEvent: UiEvent) { if (showToasts) { - Toast.makeText(context, "Analytics: ${uiEvent.javaClass.simpleName}", Toast.LENGTH_SHORT).show() + Toast.makeText( + context, + "Analytics: ${uiEvent.javaClass.simpleName}", + Toast.LENGTH_SHORT + ).show() } } } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenComponent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenComponent.kt deleted file mode 100644 index 25bd649f..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenComponent.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.badoo.mvicoredemo.ui.main.di.component - -import com.badoo.mvicoredemo.di.activityscope.module.ActivityModule -import com.badoo.mvicoredemo.di.activityscope.scope.ActivityScope -import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionComponent -import com.badoo.mvicoredemo.ui.main.MainActivity -import com.badoo.mvicoredemo.ui.main.di.module.MainScreenModule -import dagger.Component - -@ActivityScope -@Component( - dependencies = [ - UserSessionComponent::class - ], - modules = [ - ActivityModule::class, - MainScreenModule::class - ] -) -interface MainScreenComponent { - fun inject(activity: MainActivity) -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenInjector.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenInjector.kt deleted file mode 100644 index 81963b83..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/component/MainScreenInjector.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.badoo.mvicoredemo.ui.main.di.component - -import com.badoo.mvicoredemo.di.activityscope.module.ActivityModule -import com.badoo.mvicoredemo.di.usersessionscope.component.UserSessionScopedComponent -import com.badoo.mvicoredemo.ui.main.MainActivity -import com.badoo.mvicoredemo.ui.main.di.module.MainScreenModule - -object MainScreenInjector { - - fun get(activity: MainActivity): MainScreenComponent = - DaggerMainScreenComponent.builder() - .userSessionComponent(UserSessionScopedComponent.get()) - .activityModule(ActivityModule(activity)) - .mainScreenModule(MainScreenModule(activity)) - .build() -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/module/MainScreenModule.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/module/MainScreenModule.kt index 06bf1684..469e0b31 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/module/MainScreenModule.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/di/module/MainScreenModule.kt @@ -1,49 +1,44 @@ package com.badoo.mvicoredemo.ui.main.di.module -import android.content.Context -import com.badoo.feature1.Feature1 -import com.badoo.feature2.Feature2 -import com.badoo.mvicoredemo.di.activityscope.scope.ActivityScope -import com.badoo.mvicoredemo.ui.main.MainActivity +import android.app.Application +import com.badoo.mvicoredemo.di.usersessionscope.component.UserComponentEntryPoint import com.badoo.mvicoredemo.ui.main.MainActivityBindings import com.badoo.mvicoredemo.ui.main.analytics.FakeAnalyticsTracker import com.badoo.mvicoredemo.ui.main.news.NewsListener import dagger.Module import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped @Module -class MainScreenModule( - private val mainActivity: MainActivity -) { +@InstallIn(ActivityComponent::class) +class MainScreenModule { @Provides - fun mainActivity() = - mainActivity - - @Provides - @ActivityScope + @ActivityScoped fun bindings( - view: MainActivity, - feature1: Feature1, - feature2: Feature2, + application: Application, analyticsTracker: FakeAnalyticsTracker, newsListener: NewsListener - ): MainActivityBindings = - MainActivityBindings( - view = view, - feature1 = feature1, - feature2 = feature2, + ): MainActivityBindings { + val userPartsEntryPoint = UserComponentEntryPoint.get(application) + + return MainActivityBindings( + feature1 = userPartsEntryPoint.feature1(), + feature2 = userPartsEntryPoint.feature2(), analyticsTracker = analyticsTracker, newsListener = newsListener ) + } @Provides - @ActivityScope - fun analyticsTracker(context: Context) = - FakeAnalyticsTracker(context) + @ActivityScoped + fun analyticsTracker(application: Application) = + FakeAnalyticsTracker(application) @Provides - @ActivityScope - fun newsListener(context: Context) = - NewsListener(context) + @ActivityScoped + fun newsListener(application: Application) = + NewsListener(application) } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEvent.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEvent.kt index c66f792b..3faa3f77 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEvent.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEvent.kt @@ -2,6 +2,6 @@ package com.badoo.mvicoredemo.ui.main.event sealed class UiEvent { data class ButtonClicked(val idx: Int) : UiEvent() - object PlusClicked : UiEvent() - object ImageClicked : UiEvent() + data object PlusClicked : UiEvent() + data object ImageClicked : UiEvent() } diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEventTransformer1.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEventTransformer1.kt index beeed4b3..2c95c6a8 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEventTransformer1.kt +++ b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/event/UiEventTransformer1.kt @@ -1,7 +1,6 @@ package com.badoo.mvicoredemo.ui.main.event import com.badoo.feature1.Feature1 -import com.badoo.feature1.Feature1.Wish.* import com.badoo.mvicoredemo.ui.main.event.UiEvent.ButtonClicked import com.badoo.mvicoredemo.ui.main.event.UiEvent.ImageClicked import com.badoo.mvicoredemo.ui.main.event.UiEvent.PlusClicked diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/viewmodel/ViewModelTransformer1.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/viewmodel/ViewModelTransformer1.kt deleted file mode 100644 index d354c534..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/ui/main/viewmodel/ViewModelTransformer1.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.badoo.mvicoredemo.ui.main.viewmodel - -import com.badoo.feature1.Feature1 -import com.badoo.feature2.Feature2 -import com.badoo.mvicoredemo.R - -class ViewModelTransformer1 : (Feature1.State) -> ViewModel { - - override fun invoke(state1: Feature1.State): ViewModel { - return ViewModel( - buttonColors = colors(state1.activeButtonIdx), - counter = state1.counter, - imageIsLoading = false, - imageUrl = null - ) - } - - private fun colors(active: Int?): List = listOf( - if (active == 0) R.color.pink_800 else R.color.pink_500, - if (active == 1) R.color.light_blue_800 else R.color.light_blue_500, - if (active == 2) R.color.lime_800 else R.color.lime_500, - if (active == 3) R.color.yellow_800 else R.color.yellow_500 - ) -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/utils/Logger.kt b/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/utils/Logger.kt deleted file mode 100644 index 328ab7db..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/java/com/badoo/mvicoredemo/utils/Logger.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.badoo.mvicoredemo.utils - -interface Logger { - operator fun invoke(string: String) -} diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/drawable/ic_add_white.png b/mvicore-demo/mvicore-demo-app/src/main/res/drawable-xxxhdpi/ic_add_white.png similarity index 100% rename from mvicore-demo/mvicore-demo-app/src/main/res/drawable/ic_add_white.png rename to mvicore-demo/mvicore-demo-app/src/main/res/drawable-xxxhdpi/ic_add_white.png diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_lifecycle_demo.xml b/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_lifecycle_demo.xml index c57036ec..c16d8831 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_lifecycle_demo.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_lifecycle_demo.xml @@ -31,7 +31,7 @@ android:id="@+id/navigationView" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="left" + android:layout_gravity="start" android:fitsSystemWindows="true" app:menu="@menu/main_drawer_menu" /> diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_main.xml b/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_main.xml index 06b7cea9..69a54c33 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_main.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/layout/activity_main.xml @@ -166,7 +166,7 @@ android:id="@+id/navigationView" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="left" + android:layout_gravity="start" android:fitsSystemWindows="true" app:menu="@menu/main_drawer_menu"/> diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6b78462d..b3e26b4c 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6b78462d..b3e26b4c 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ + diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/values/colors.xml b/mvicore-demo/mvicore-demo-app/src/main/res/values/colors.xml index abb243a7..aa2b83de 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/values/colors.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/values/colors.xml @@ -3,7 +3,4 @@ @color/grey_800 @color/grey_900 #FF4081 - - #a3951a - #778c20 diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/values/colors_material.xml b/mvicore-demo/mvicore-demo-app/src/main/res/values/colors_material.xml old mode 100755 new mode 100644 index 5de86fb7..0a5237da --- a/mvicore-demo/mvicore-demo-app/src/main/res/values/colors_material.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/values/colors_material.xml @@ -1,300 +1,17 @@ - - #fde0dc - #f9bdbb - #f69988 - #f36c60 - #e84e40 - #e51c23 - #dd191d - #d01716 - #c41411 - #b0120a - #ff7997 - #ff5177 - #ff2d6f - #e00032 - - - #fce4ec - #f8bbd0 - #f48fb1 - #f06292 - #ec407a #e91e63 - #d81b60 - #c2185b #ad1457 - #880e4f - #ff80ab - #ff4081 - #f50057 - #c51162 - - - #f3e5f5 - #e1bee7 - #cd93d8 - #ba68c8 - #ab47bc - #9c27b0 - #8e24aa - #7b1fa2 - #6a1b9a - #4a148c - #ea80fc - #e040fb - #d500f9 - #aa00ff - - - #ede7f6 - #d1c4e9 - #b39ddb - #9575cd - #7e57c2 - #673ab7 - #5e35b1 - #512da8 - #4527a0 - #311b92 - #b388ff - #7c4dff - #651fff - #6200ea - - - #e8eaf6 - #c5cae9 - #9fa8da - #7986cb - #5c6bc0 - #3f51b5 - #3949ab - #303f9f - #283593 - #1a237e - #8c9eff - #536dfe - #3d5afe - #304ffe - - #e7e9fd - #d0d9ff - #afbfff - #91a7ff - #738ffe - #5677fc - #4e6cef - #455ede - #3b50ce - #2a36b1 - #a6baff - #6889ff - #4d73ff - #4d69ff - - - #e1f5f3 - #b3e5fc - #81d4fa - #4fc3f7 - #29b6f6 #03a9f4 - #039be5 - #0288d1 #0277bd - #01579b - #80d8ff - #40c4ff - #00b0ff - #0091ea - - - #e0f7fa - #b2ebf2 - #80deea - #4dd0e1 - #26c6da - #00bcd4 - #00acc1 - #0097a7 - #00838f - #006064 - #84ffff - #18ffff - #00e5ff - #00b8d4 - - - #e0f2f1 - #b2dfdb - #80cbc4 - #4db6ac - #26a69a - #009688 - #00897b - #00796b - #00695c - #004d40 - #a7ffeb - #64ffda - #1de9b6 - #00bfa5 - - - #d0f8ce - #a3e9a4 - #72d572 - #42bd41 - #2baf2b - #259b24 - #0a8f08 - #0a7e07 - #056f00 - #0d5302 - #a2f78d - #5af158 - #14e715 - #12c700 - - #f1f8e9 - #dcedc8 - #c5e1a5 - #aed581 - #9ccc65 - #8bc34a - #7cb342 - #689f38 - #558b2f - #33691e - #ccff90 - #b2ff59 - #76ff03 - #64dd17 - - - #f9fbe7 - #f0f4c3 - #e6ee9c - #dce775 - #d4e157 #cddc39 - #c0ca33 - #afb42b #9e9d24 - #827717 - #f4ff81 - #eeff41 - #c6ff00 - #aeea00 - - #fffde7 - #fff9c4 - #fff59d - #fff176 - #ffee58 #ffeb3b - #fdd835 - #fbc02d #f9a825 - #f57f17 - #ffff8d - #ffff00 - #ffea00 - #ffd600 - - - #fff8e1 - #ffecb3 - #ffe082 - #ffd54f - #ffca28 - #ffc107 - #ffb300 - #ffa000 - #ff8f00 - #ff6f00 - #ffe57f - #ffd740 - #ffc400 - #ffab00 - - - #fff3e0 - #ffe0b2 - #ffcc80 - #ffb74d - #ffa726 - #ff9800 - #fb8c00 - #f57c00 - #ef6c00 - #e65100 - #ffd180 - #ffab40 - #ff9100 - #ff6d00 - - - #fbe9e7 - #ffccbc - #ffab91 - #ff8a65 - #ff7043 - #ff5722 - #f4511e - #e64a19 - #d84315 - #bf360c - #ff9e80 - #ff6e40 - #ff3d00 - #dd2c00 - - #efebe9 - #d7ccc8 - #bcaaa4 - #a1887f - #8d6e63 - #795548 - #6d4c41 - #5d4037 - #4e342e - #3e2723 - - - #fafafa - #f5f5f5 - #eeeeee - #e0e0e0 - #bdbdbd - #9e9e9e - #757575 - #616161 #424242 #212121 - - - #eceff1 - #cfd8dc - #b0bec5 - #90a4ae - #78909c - #607d8b - #546e7a - #455a64 - #37474f - #263238 - - - #000000 - #ffffff - #ddffffff - - \ No newline at end of file + diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/values/dimens.xml b/mvicore-demo/mvicore-demo-app/src/main/res/values/dimens.xml deleted file mode 100644 index ec8865ba..00000000 --- a/mvicore-demo/mvicore-demo-app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ - - 160dp - - 16dp - 16dp - diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/values/strings.xml b/mvicore-demo/mvicore-demo-app/src/main/res/values/strings.xml index 55228c05..35e9910e 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/values/strings.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/values/strings.xml @@ -1,13 +1,13 @@ - + MVICore Demo Sign in Sign out - Ok + OK Main Lifecycle Toasts - + UI controls
    diff --git a/mvicore-demo/mvicore-demo-app/src/main/res/values/styles.xml b/mvicore-demo/mvicore-demo-app/src/main/res/values/styles.xml index f6c32dac..8db5100f 100644 --- a/mvicore-demo/mvicore-demo-app/src/main/res/values/styles.xml +++ b/mvicore-demo/mvicore-demo-app/src/main/res/values/styles.xml @@ -22,16 +22,4 @@ true - - diff --git a/mvicore-demo/mvicore-demo-catapi/build.gradle b/mvicore-demo/mvicore-demo-catapi/build.gradle deleted file mode 100644 index 78373c66..00000000 --- a/mvicore-demo/mvicore-demo-catapi/build.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 30 - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - implementation deps('androidx.appcompat:appcompat') - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxandroid') - - implementation deps('com.squareup.retrofit2:retrofit') - implementation deps('com.squareup.retrofit2:adapter-rxjava2') - implementation deps('com.squareup.retrofit2:converter-simplexml') - - configurations { - all*.exclude group: 'xpp3', module: 'xpp3' - } -} diff --git a/mvicore-demo/mvicore-demo-catapi/build.gradle.kts b/mvicore-demo/mvicore-demo-catapi/build.gradle.kts new file mode 100644 index 00000000..ba4397b4 --- /dev/null +++ b/mvicore-demo/mvicore-demo-catapi/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +android { + namespace = "com.badoo.catapi" + compileSdk = 34 + + defaultConfig { + minSdk = 21 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + api(libs.rxjava2) + api(libs.retrofit.runtime) + + implementation(libs.kotlin.stdlib) + implementation(libs.retrofit.adapter.rxjava2) + implementation(libs.retrofit.converter.simplexml) + + configurations { + all { + exclude(group = "xpp3", module = "xpp3") + } + } +} diff --git a/mvicore-demo/mvicore-demo-catapi/lint-baseline.xml b/mvicore-demo/mvicore-demo-catapi/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-demo/mvicore-demo-catapi/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-demo/mvicore-demo-catapi/src/main/AndroidManifest.xml b/mvicore-demo/mvicore-demo-catapi/src/main/AndroidManifest.xml deleted file mode 100644 index 5330f6dc..00000000 --- a/mvicore-demo/mvicore-demo-catapi/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/mvicore-demo/mvicore-demo-catapi/src/main/res/values/strings.xml b/mvicore-demo/mvicore-demo-catapi/src/main/res/values/strings.xml deleted file mode 100644 index 6e4f1c22..00000000 --- a/mvicore-demo/mvicore-demo-catapi/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - catapi - diff --git a/mvicore-demo/mvicore-demo-feature1/build.gradle b/mvicore-demo/mvicore-demo-feature1/build.gradle deleted file mode 100644 index 5c639b50..00000000 --- a/mvicore-demo/mvicore-demo-feature1/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion 30 - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -androidExtensions { - experimental = true -} - -dependencies { - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - implementation deps('androidx.appcompat:appcompat') - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxandroid') - - implementation project(":mvicore") -} diff --git a/mvicore-demo/mvicore-demo-feature1/build.gradle.kts b/mvicore-demo/mvicore-demo-feature1/build.gradle.kts new file mode 100644 index 00000000..15c32b1f --- /dev/null +++ b/mvicore-demo/mvicore-demo-feature1/build.gradle.kts @@ -0,0 +1,38 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +android { + namespace = "com.badoo.feature1" + compileSdk = 34 + + defaultConfig { + minSdk = 21 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + api(project(":mvicore")) + + implementation(libs.kotlin.stdlib) +} diff --git a/mvicore-demo/mvicore-demo-feature1/lint-baseline.xml b/mvicore-demo/mvicore-demo-feature1/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-demo/mvicore-demo-feature1/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-demo/mvicore-demo-feature1/src/main/AndroidManifest.xml b/mvicore-demo/mvicore-demo-feature1/src/main/AndroidManifest.xml deleted file mode 100644 index ab0c2ac6..00000000 --- a/mvicore-demo/mvicore-demo-feature1/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/mvicore-demo/mvicore-demo-feature1/src/main/java/com/badoo/feature1/Feature1.kt b/mvicore-demo/mvicore-demo-feature1/src/main/java/com/badoo/feature1/Feature1.kt index dd942e5b..84b7fdbc 100644 --- a/mvicore-demo/mvicore-demo-feature1/src/main/java/com/badoo/feature1/Feature1.kt +++ b/mvicore-demo/mvicore-demo-feature1/src/main/java/com/badoo/feature1/Feature1.kt @@ -17,7 +17,7 @@ class Feature1 : ReducerFeature( ) sealed class Wish { - object IncreaseCounter : Wish() + data object IncreaseCounter : Wish() data class SetActiveButton(val idx: Int) : Wish() } diff --git a/mvicore-demo/mvicore-demo-feature1/src/main/res/values/strings.xml b/mvicore-demo/mvicore-demo-feature1/src/main/res/values/strings.xml deleted file mode 100644 index bbea6f56..00000000 --- a/mvicore-demo/mvicore-demo-feature1/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - feature1 - diff --git a/mvicore-demo/mvicore-demo-feature2/build.gradle b/mvicore-demo/mvicore-demo-feature2/build.gradle deleted file mode 100644 index b1f336e1..00000000 --- a/mvicore-demo/mvicore-demo-feature2/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion 30 - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 30 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -androidExtensions { - experimental = true -} - -dependencies { - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - implementation deps('androidx.appcompat:appcompat') - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxandroid') - - implementation project(":mvicore") - implementation project(":mvicore-android") - implementation project(':mvicore-demo:mvicore-demo-catapi') -} diff --git a/mvicore-demo/mvicore-demo-feature2/build.gradle.kts b/mvicore-demo/mvicore-demo-feature2/build.gradle.kts new file mode 100644 index 00000000..75c12d23 --- /dev/null +++ b/mvicore-demo/mvicore-demo-feature2/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-parcelize") + id("mvi-core-lint") + id("mvi-core-detekt") +} + +android { + namespace = "com.badoo.feature2" + compileSdk = 34 + + defaultConfig { + minSdk = 21 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +dependencies { + api(libs.rxjava2) + api(project(":mvicore")) + api(project(":mvicore-demo:mvicore-demo-catapi")) + + implementation(project(":mvicore-android")) + implementation(libs.kotlin.stdlib) +} diff --git a/mvicore-demo/mvicore-demo-feature2/detekt-baseline.xml b/mvicore-demo/mvicore-demo-feature2/detekt-baseline.xml new file mode 100644 index 00000000..24c2d879 --- /dev/null +++ b/mvicore-demo/mvicore-demo-feature2/detekt-baseline.xml @@ -0,0 +1,8 @@ + + + + + MagicNumber:Extensions.kt$10 + TooGenericExceptionThrown:Extensions.kt$throw RuntimeException("Test exception") + + diff --git a/mvicore-demo/mvicore-demo-feature2/lint-baseline.xml b/mvicore-demo/mvicore-demo-feature2/lint-baseline.xml new file mode 100644 index 00000000..03c0924e --- /dev/null +++ b/mvicore-demo/mvicore-demo-feature2/lint-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mvicore-demo/mvicore-demo-feature2/src/main/AndroidManifest.xml b/mvicore-demo/mvicore-demo-feature2/src/main/AndroidManifest.xml deleted file mode 100644 index 496ea830..00000000 --- a/mvicore-demo/mvicore-demo-feature2/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/mvicore-demo/mvicore-demo-feature2/src/main/java/com/badoo/feature2/Feature2.kt b/mvicore-demo/mvicore-demo-feature2/src/main/java/com/badoo/feature2/Feature2.kt index d339fa2f..ec4ea30f 100644 --- a/mvicore-demo/mvicore-demo-feature2/src/main/java/com/badoo/feature2/Feature2.kt +++ b/mvicore-demo/mvicore-demo-feature2/src/main/java/com/badoo/feature2/Feature2.kt @@ -20,7 +20,7 @@ import com.badoo.mvicore.element.TimeCapsule import com.badoo.mvicore.feature.ActorReducerFeature import io.reactivex.Observable import io.reactivex.Observable.just -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize class Feature2( timeCapsule: TimeCapsule? = null @@ -45,11 +45,11 @@ class Feature2( ) : Parcelable sealed class Wish { - object LoadNewImage : Wish() + data object LoadNewImage : Wish() } sealed class Effect { - object StartedLoading : Effect() + data object StartedLoading : Effect() data class LoadedImage(val url: String) : Effect() data class ErrorLoading(val throwable: Throwable) : Effect() } diff --git a/mvicore-demo/mvicore-demo-feature2/src/main/res/values/strings.xml b/mvicore-demo/mvicore-demo-feature2/src/main/res/values/strings.xml deleted file mode 100644 index a56216fd..00000000 --- a/mvicore-demo/mvicore-demo-feature2/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - feature2 - diff --git a/mvicore-diff/build.gradle b/mvicore-diff/build.gradle deleted file mode 100644 index 56cc9294..00000000 --- a/mvicore-diff/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - - -group = 'com.github.badoo.mvicore' - -dependencies { - def deps = rootProject.ext.deps - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -repositories { - jcenter() -} - -sourceSets { - main { - java {} - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -task packageJavadoc(type: Jar, dependsOn: javadoc) { - from javadoc.outputDirectory - classifier = 'javadoc' -} - -artifacts { - archives packageSources - archives packageJavadoc -} -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} diff --git a/mvicore-diff/build.gradle.kts b/mvicore-diff/build.gradle.kts new file mode 100644 index 00000000..18468780 --- /dev/null +++ b/mvicore-diff/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("java") + id("mvi-core-publish-java") + id("kotlin") + id("org.jetbrains.dokka") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType { + useJUnitPlatform() +} + +dependencies { + implementation(libs.kotlin.stdlib) + + testRuntimeOnly(libs.junit5.engine) + testImplementation(libs.junit5.api) +} diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt index 6875246b..b4d23e08 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/HelperTest.kt @@ -2,10 +2,11 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Model import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class HelperTest { + @Test fun `by value strategy compares by value`() { val results = testWatcher, Model>( diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt index a688bed3..bcdfa488 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/ModelWatcherTest.kt @@ -2,8 +2,8 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Model import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class ModelWatcherTest { @@ -103,7 +103,7 @@ class ModelWatcherTest { } @Test - fun `invokes callback with combined diffStrategy using "or"`() { + fun `invokes callback with combined diffStrategy using 'or'`() { val results = testWatcher( listOf( Model(list = listOf(""), int = 1), @@ -120,7 +120,7 @@ class ModelWatcherTest { } @Test - fun `invokes callback with combined diffStrategy using "and"`() { + fun `invokes callback with combined diffStrategy using 'and'`() { val results = testWatcher( listOf( Model(list = listOf(""), int = 1), diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt index 463afd37..92ce8d79 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/SealedClassTest.kt @@ -3,10 +3,11 @@ package com.badoo.mvicore import com.badoo.mvicore.util.Nested import com.badoo.mvicore.util.SealedModel import com.badoo.mvicore.util.testWatcher -import org.junit.Test -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class SealedClassTest { + @Test fun `sealed class subtypes are triggered every time type has changed`() { val results = testWatcher, SealedModel>( @@ -16,7 +17,7 @@ class SealedClassTest { SealedModel.Value(list = listOf("")) ) ) { updates -> - type { + type { SealedModel.Value::list { updates += it } @@ -56,7 +57,7 @@ class SealedClassTest { SealedModel.Value() ) ) { updates -> - type { + type { SealedModel.Value::list { updates += it } @@ -85,7 +86,7 @@ class SealedClassTest { ) ) { updates -> type { - type { + type { Nested.SubNested.Value::list { updates += it } diff --git a/mvicore-diff/src/test/java/com/badoo/mvicore/util/SealedModel.kt b/mvicore-diff/src/test/java/com/badoo/mvicore/util/SealedModel.kt index 2b103a65..2b94eabe 100644 --- a/mvicore-diff/src/test/java/com/badoo/mvicore/util/SealedModel.kt +++ b/mvicore-diff/src/test/java/com/badoo/mvicore/util/SealedModel.kt @@ -9,7 +9,7 @@ sealed class SealedModel { val nullable: Boolean? = null ): SealedModel() - object Nothing: SealedModel() { + data object Nothing: SealedModel() { override val list: List = emptyList() } } @@ -17,7 +17,7 @@ sealed class SealedModel { sealed class Nested { sealed class SubNested : Nested() { data class Value(val list: List) : SubNested() - object Nothing : SubNested() + data object Nothing : SubNested() } - object Something: Nested() + data object Something: Nested() } diff --git a/mvicore-plugin/common/build.gradle b/mvicore-plugin/common/build.gradle deleted file mode 100644 index 8d1f62f6..00000000 --- a/mvicore-plugin/common/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -apply plugin: 'kotlin' -apply plugin: 'maven' - -archivesBaseName = 'mvicore-plugin-common' - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - -dependencies { - def deps = rootProject.ext.deps - implementation deps('com.google.code.gson:gson') - - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -task packageJavadoc(type: Jar, dependsOn: javadoc) { - from javadoc.outputDirectory - classifier = 'javadoc' -} - -artifacts { - archives packageSources - archives packageJavadoc -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} diff --git a/mvicore-plugin/common/build.gradle.kts b/mvicore-plugin/common/build.gradle.kts new file mode 100644 index 00000000..c5ebc566 --- /dev/null +++ b/mvicore-plugin/common/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("kotlin") + id("mvi-core-publish-java") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +base.archivesBaseName = "mvicore-plugin-common" + +dependencies { + api(libs.gson) + + implementation(libs.kotlin.stdlib) +} diff --git a/mvicore-plugin/common/src/main/java/com/badoo/mvicore/plugin/model/Event.kt b/mvicore-plugin/common/src/main/java/com/badoo/mvicore/plugin/model/Event.kt index 5fdefd02..6073a050 100644 --- a/mvicore-plugin/common/src/main/java/com/badoo/mvicore/plugin/model/Event.kt +++ b/mvicore-plugin/common/src/main/java/com/badoo/mvicore/plugin/model/Event.kt @@ -25,5 +25,5 @@ sealed class Event(val type: String) { val name: String ) : Event("connect") - object Ping: Event("ping") + data object Ping: Event("ping") } diff --git a/mvicore-plugin/idea/build.gradle b/mvicore-plugin/idea/build.gradle deleted file mode 100644 index 869c609b..00000000 --- a/mvicore-plugin/idea/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -plugins { - id "org.jetbrains.intellij" version "0.4.10" -} - -apply plugin: 'java' -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.intellij' -apply plugin: 'idea' - -group = 'com.github.badoo.mvicore' -archivesBaseName = 'mvicore-plugin-idea' -version = '0.0.2' - -intellij { - pluginName 'mvicore-plugin' - version "2019.2" - plugins = ['android'] -} - -patchPluginXml { - sinceBuild '145.0' -} - -dependencies { - def deps = rootProject.ext.deps - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - implementation deps('com.google.code.gson:gson') - implementation project(':mvicore-plugin:common') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -repositories { - jcenter() - -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -artifacts { - archives packageSources -} - -jar { - from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } -} diff --git a/mvicore-plugin/idea/build.gradle.kts b/mvicore-plugin/idea/build.gradle.kts new file mode 100644 index 00000000..985b49a9 --- /dev/null +++ b/mvicore-plugin/idea/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("java") + id("kotlin") + id("org.jetbrains.intellij") version "1.17.3" + id("idea") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +group = "com.github.badoo.mvicore" +base.archivesBaseName = "mvicore-plugin-idea" +version = "0.0.2" + +// Required as the "intellij" plugin is overriding the repositories from "settings.gradle" +repositories { + google() + mavenCentral() +} + +// TODO: Publish new version - https://github.com/badoo/MVICore/issues/170 +intellij { + pluginName.set("mvicore-plugin") + version.set("2021.2.1") + plugins.set(listOf("android")) +} + +tasks { + patchPluginXml { + sinceBuild.set("145.0") + } +} + +dependencies { + api(project(":mvicore-plugin:common")) + api(libs.gson) + api(libs.rxjava2) + + implementation(libs.rxkotlin) + implementation(libs.kotlin.stdlib) +} diff --git a/mvicore-plugin/idea/detekt-baseline.xml b/mvicore-plugin/idea/detekt-baseline.xml new file mode 100644 index 00000000..00de1004 --- /dev/null +++ b/mvicore-plugin/idea/detekt-baseline.xml @@ -0,0 +1,24 @@ + + + + + EmptyCatchBlock:SocketObservable.kt$SocketObservable.<no name provided>${ } + EmptyFunctionBlock:SocketObservable.kt${ } + ForbiddenComment:Adb.kt$//TODO: Select device? + ForbiddenComment:ConnectionList.kt$ConnectionList$// TODO: make more understandable + MagicNumber:MviPluginToolWindowFactory.kt$MviPluginToolWindowFactory$7675 + MaxLineLength:EventList.kt$EventList.CellRenderer$override + MaxLineLength:MviPluginToolWindowFactory.kt$MviPluginToolWindowFactory$right.secondComponent = JBScrollPane(currentElement, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED) + MaxLineLength:MviPluginToolWindowFactory.kt$MviPluginToolWindowFactory$val sideActionsBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.COMMANDER_TOOLBAR, sideActions, false) + NestedBlockDepth:RunAction.kt$RunAction$private fun JsonElement.parse(): Triple<Long?, String?, JsonElement> + NestedBlockDepth:SocketObservable.kt$SocketObservable.<no name provided>$override fun run() + ReturnCount:Adb.kt$fun forwardPort(project: Project, port: Int): Boolean + ReturnCount:Adb.kt$fun stopForwarding(project: Project, port: Int): Boolean + SwallowedException:SocketObservable.kt$SocketObservable.<no name provided>$e: SocketException + TooGenericExceptionCaught:SocketObservable.kt$SocketObservable.<no name provided>$e: Exception + UnusedPrivateMember:MviPluginToolWindowFactory.kt$MviPluginToolWindowFactory$private val logger = Logger.getInstance(javaClass) + VariableNaming:RunAction.kt$RunAction$private val TIMESTAMP = "\$timestamp" + VariableNaming:RunAction.kt$RunAction$private val TYPE = "\$type" + VariableNaming:RunAction.kt$RunAction$private val VALUE = "\$value" + + diff --git a/mvicore-plugin/idea/src/main/java/com/badoo/mvicore/plugin/utils/MainThreadScheduler.kt b/mvicore-plugin/idea/src/main/java/com/badoo/mvicore/plugin/utils/MainThreadScheduler.kt index 6819ebb9..78437323 100644 --- a/mvicore-plugin/idea/src/main/java/com/badoo/mvicore/plugin/utils/MainThreadScheduler.kt +++ b/mvicore-plugin/idea/src/main/java/com/badoo/mvicore/plugin/utils/MainThreadScheduler.kt @@ -5,4 +5,4 @@ import javax.swing.SwingUtilities val mainThreadScheduler = Schedulers.from { SwingUtilities.invokeLater { it.run() } -} \ No newline at end of file +} diff --git a/mvicore-plugin/middleware/build.gradle b/mvicore-plugin/middleware/build.gradle deleted file mode 100644 index e23a9e4a..00000000 --- a/mvicore-plugin/middleware/build.gradle +++ /dev/null @@ -1,63 +0,0 @@ -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -archivesBaseName = 'mvicore-plugin-middleware' - -dependencies { - def deps = rootProject.ext.deps - - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - implementation deps('com.google.code.gson:gson') - implementation project(':mvicore') - implementation project(':mvicore-plugin:common') - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -repositories { - jcenter() -} - -sourceSets { - main { - java {} - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -task packageJavadoc(type: Jar, dependsOn: javadoc) { - from javadoc.outputDirectory - classifier = 'javadoc' -} - -artifacts { - archives packageSources - archives packageJavadoc -} -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} -compileTestKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} diff --git a/mvicore-plugin/middleware/build.gradle.kts b/mvicore-plugin/middleware/build.gradle.kts new file mode 100644 index 00000000..df635d3a --- /dev/null +++ b/mvicore-plugin/middleware/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + id("java") + id("mvi-core-publish-java") + id("kotlin") + id("org.jetbrains.dokka") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +base.archivesBaseName = "mvicore-plugin-middleware" + +dependencies { + api(project(":binder")) + api(project(":mvicore-plugin:common")) + api(libs.rxjava2) + api(libs.gson) + + implementation(libs.kotlin.stdlib) + implementation(libs.rxkotlin) +} diff --git a/mvicore-plugin/middleware/detekt-baseline.xml b/mvicore-plugin/middleware/detekt-baseline.xml new file mode 100644 index 00000000..9c2cb242 --- /dev/null +++ b/mvicore-plugin/middleware/detekt-baseline.xml @@ -0,0 +1,12 @@ + + + + + ForbiddenComment:DefaultPluginStore.kt$DefaultPluginStore$// TODO: log? + MagicNumber:PluginSocketThread.kt$PluginSocketThread$100 + RethrowCaughtException:PluginSocketThread.kt$PluginSocketThread$throw e + SwallowedException:PluginSocketThread.kt$PluginSocketThread$e: IOException + TooGenericExceptionCaught:PluginSocketThread.kt$PluginSocketThread$e: Exception + UnusedPrivateMember:DefaultPluginStore.kt$DefaultPluginStore$event: PluginSocketThread.Connected + + diff --git a/mvicore-plugin/templates/build.gradle b/mvicore-plugin/templates/build.gradle deleted file mode 100644 index 2abaebe1..00000000 --- a/mvicore-plugin/templates/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id "org.jetbrains.intellij" version "0.6.3" -} - -apply plugin: 'org.jetbrains.intellij' - -group 'com.badoo.mvicore' -version '0.8' - -intellij { - pluginName 'MVICoreFileTemplates' - version "2019.2" - plugins = ['android'] -} - -patchPluginXml { - changeNotes - """ - 0.8 - Fixed plugin work for Android Studio 4.1 - """ - sinceBuild '145.0' - untilBuild null -} - -repositories { - mavenCentral() -} diff --git a/mvicore-plugin/templates/build.gradle.kts b/mvicore-plugin/templates/build.gradle.kts new file mode 100644 index 00000000..09793fb3 --- /dev/null +++ b/mvicore-plugin/templates/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + id("org.jetbrains.intellij") version "1.17.3" +} + +group = "com.badoo.mvicore" +version = "0.8" + +// Required as the "intellij" plugin is overriding the repositories from "settings.gradle" +repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") +} + +// TODO: Publish new version - https://github.com/badoo/MVICore/issues/170 +intellij { + pluginName.set("MVICoreFileTemplates") + version.set("2021.2.1") + plugins.set(listOf("android")) +} + +tasks { + patchPluginXml { + changeNotes.set( + """ + 0.8 + Fixed plugin work for Android Studio 4.1 + """ + ) + sinceBuild.set("145.0") + untilBuild.set(null as String?) + } +} diff --git a/mvicore/build.gradle b/mvicore/build.gradle deleted file mode 100644 index 9865120d..00000000 --- a/mvicore/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -apply plugin: 'java' -apply plugin: 'maven' -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -group = 'com.github.badoo.mvicore' - -dependencies { - def deps = rootProject.ext.deps - - api project(":binder") - implementation deps('io.reactivex.rxjava2:rxjava') - implementation deps('io.reactivex.rxjava2:rxkotlin') - implementation deps("org.jetbrains.kotlin:kotlin-stdlib-jdk7") - - testImplementation deps('junit:junit') - testImplementation deps('org.jetbrains.kotlin:kotlin-test-junit') - testImplementation deps('org.mockito.kotlin:mockito-kotlin') -} - -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 - -compileKotlin { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 - } -} - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion" - } -} - -repositories { - jcenter() -} - -sourceSets { - main { - java {} - } -} - -task packageSources(type: Jar, dependsOn: 'classes') { - classifier = 'sources' - from sourceSets.main.allSource -} - -task packageJavadoc(type: Jar, dependsOn: javadoc) { - from javadoc.outputDirectory - classifier = 'javadoc' -} - -artifacts { - archives packageSources - archives packageJavadoc -} diff --git a/mvicore/build.gradle.kts b/mvicore/build.gradle.kts new file mode 100644 index 00000000..56b68d3f --- /dev/null +++ b/mvicore/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("java") + id("mvi-core-publish-java") + id("kotlin") + id("org.jetbrains.dokka") + id("mvi-core-detekt") +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +tasks.withType { + useJUnitPlatform() +} + +dependencies { + api(project(":binder")) + api(libs.rxjava2) + implementation(libs.rxkotlin) + implementation(libs.kotlin.stdlib) + + testRuntimeOnly(libs.junit5.engine) + testImplementation(libs.junit5.api) + testImplementation(libs.junit5.params) + testImplementation(libs.mockito.kotlin) +} diff --git a/mvicore/detekt-baseline.xml b/mvicore/detekt-baseline.xml new file mode 100644 index 00000000..39ed1598 --- /dev/null +++ b/mvicore/detekt-baseline.xml @@ -0,0 +1,25 @@ + + + + + LongParameterList:AsyncBaseFeatureTest.kt$AsyncBaseFeatureTest$( featureScheduler: Scheduler = this.featureScheduler, observationScheduler: Scheduler = this.observationScheduler, bootstrapper: Bootstrapper<Action>? = { Observable.just(Action()).observeOn(Schedulers.single()) }, wishToAction: WishToAction<Wish, Action> = { Action() }, actor: Actor<State, Action, Effect> = { _, _ -> Observable.just(Effect()).observeOn(Schedulers.single()) }, reducer: Reducer<State, Effect> = { _, _ -> State() }, postProcessor: PostProcessor<Action, Effect, State> = { _, _, _ -> null }, newsPublisher: NewsPublisher<Action, Effect, State, News> = { _, _, _ -> News() } ) + LongParameterList:BaseAsyncFeature.kt$BaseAsyncFeature$( initialState: State, bootstrapper: Bootstrapper<Action>? = null, private val wishToAction: WishToAction<Wish, Action>, actor: Actor<State, Action, Effect>, reducer: Reducer<State, Effect>, postProcessor: PostProcessor<Action, Effect, State>? = null, newsPublisher: NewsPublisher<Action, Effect, State, News>? = null, private val schedulers: AsyncFeatureSchedulers ) + LongParameterList:BaseFeature.kt$BaseFeature$( initialState: State, bootstrapper: Bootstrapper<Action>? = null, private val wishToAction: WishToAction<Wish, Action>, actor: Actor<State, Action, Effect>, reducer: Reducer<State, Effect>, postProcessor: PostProcessor<Action, Effect, State>? = null, newsPublisher: NewsPublisher<Action, Effect, State, News>? = null, private val featureScheduler: FeatureScheduler? = null ) + MaxLineLength:AsyncBaseFeatureTest.kt$AsyncBaseFeatureTest$feature = testFeature(featureScheduler = Schedulers.trampoline(), observationScheduler = Schedulers.trampoline()) + MaxLineLength:BaseAsyncFeature.kt$BaseAsyncFeature.ActorWrapper$// Remove disposables manually because CompositeDisposable does not do it automatically producing memory leaks + MaxLineLength:BaseFeature.kt$BaseFeature.ActorWrapper$// Remove disposables manually because CompositeDisposable does not do it automatically producing memory leaks + MaxLineLength:BaseFeaturePostProcessorTest.kt$BaseFeaturePostProcessorTest$fun + MaxLineLength:BaseFeatureWithSchedulerTest.kt$BaseFeatureWithSchedulerTest$fun + MaxLineLength:BaseFeatureWithoutSchedulerTest.kt$BaseFeatureWithoutSchedulerTest$fun + MaxLineLength:BootstrapperTest.kt$BootstrapperTest$fun + MaxLineLength:MemoryRecordStore.kt$MemoryRecordStore$override + MaxLineLength:NewsPublishingTest.kt$NewsPublishingTest$fun + MaxLineLength:NewsPublishingTest.kt$Parameter$override fun toString(): String + MaxLineLength:NewsPublishingTest.kt$private fun <T : Any> createMiddlewareStub(consumer: Consumer<T>): ConsumerMiddleware<T> + MaxLineLength:PlaybackMiddleware.kt$PlaybackMiddleware.RecordStore$fun <Out: Any, In: Any> record(middleware: PlaybackMiddleware<Out, In>, endpoints: Connection<Out, In>, element: In) + MaxLineLength:ReducerFeature.kt$ReducerFeature.SimpleNewsPublisher$abstract + MaxLineLength:SameThreadVerifierTest.kt$SameThreadVerifierTest$"java.lang.String was interacted with on the wrong thread. Expected: '$testWorkerThreadName', Actual: 'wrong-thread'" + ThrowingExceptionFromFinally:RxErrorRule.kt$RxErrorRule.<no name provided>$throw CompositeException(errors) + UseCheckOrError:MemoryRecordStore.kt$MemoryRecordStore$throw IllegalStateException("Trying to playback while still recording") + + diff --git a/mvicore/src/main/java/com/badoo/mvicore/extension/Rx.kt b/mvicore/src/main/java/com/badoo/mvicore/extension/Rx.kt index e0722ac0..acf1ed82 100644 --- a/mvicore/src/main/java/com/badoo/mvicore/extension/Rx.kt +++ b/mvicore/src/main/java/com/badoo/mvicore/extension/Rx.kt @@ -5,7 +5,7 @@ import io.reactivex.Observer import io.reactivex.Scheduler import io.reactivex.functions.Consumer -fun Observer.asConsumer() = Consumer { onNext(it) } +fun Observer.asConsumer() = Consumer { onNext(it) } internal fun Observable.subscribeOnNullable(scheduler: Scheduler?): Observable = if (scheduler != null) subscribeOn(scheduler) else this diff --git a/mvicore/src/main/java/com/badoo/mvicore/feature/AsyncFeatureSchedulers.kt b/mvicore/src/main/java/com/badoo/mvicore/feature/AsyncFeatureSchedulers.kt index 42d24171..bf55ee90 100644 --- a/mvicore/src/main/java/com/badoo/mvicore/feature/AsyncFeatureSchedulers.kt +++ b/mvicore/src/main/java/com/badoo/mvicore/feature/AsyncFeatureSchedulers.kt @@ -9,4 +9,4 @@ class AsyncFeatureSchedulers( /** Should be single-threaded. */ val featureScheduler: Scheduler, val observationScheduler: Scheduler -) \ No newline at end of file +) diff --git a/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt b/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt index 050961fc..0ee93be6 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/TestHelper.kt @@ -59,36 +59,36 @@ class TestHelper { } sealed class TestWish { - object Unfulfillable : TestWish() - object MaybeFulfillable : TestWish() - object FulfillableInstantly1 : TestWish() - object FulfillableInstantly2 : TestWish() + data object Unfulfillable : TestWish() + data object MaybeFulfillable : TestWish() + data object FulfillableInstantly1 : TestWish() + data object FulfillableInstantly2 : TestWish() data class FulfillableAsync(val delayMs: Long) : TestWish() - object TranslatesTo3Effects : TestWish() - object LoopbackWishInitial : TestWish() - object LoopbackWish1 : TestWish() - object LoopbackWish2 : TestWish() - object LoopbackWish3 : TestWish() + data object TranslatesTo3Effects : TestWish() + data object LoopbackWishInitial : TestWish() + data object LoopbackWish1 : TestWish() + data object LoopbackWish2 : TestWish() + data object LoopbackWish3 : TestWish() data class IncreasCounterBy(val value: Int) : TestWish() } sealed class TestEffect { - object StartedAsync : TestEffect() + data object StartedAsync : TestEffect() data class InstantEffect(val amount: Int) : TestEffect() data class FinishedAsync(val amount: Int) : TestEffect() data class ConditionalThingHappened(val multiplier: Int) : TestEffect() - object MultipleEffect1 : TestEffect() - object MultipleEffect2 : TestEffect() - object MultipleEffect3 : TestEffect() - object LoopbackEffectInitial : TestEffect() - object LoopbackEffect1 : TestEffect() - object LoopbackEffect2 : TestEffect() - object LoopbackEffect3 : TestEffect() + data object MultipleEffect1 : TestEffect() + data object MultipleEffect2 : TestEffect() + data object MultipleEffect3 : TestEffect() + data object LoopbackEffectInitial : TestEffect() + data object LoopbackEffect1 : TestEffect() + data object LoopbackEffect2 : TestEffect() + data object LoopbackEffect3 : TestEffect() } sealed class TestNews { - object ConditionalThingHappened : TestNews() - object Loopback : TestNews() + data object ConditionalThingHappened : TestNews() + data object Loopback : TestNews() } class TestActor( @@ -114,7 +114,7 @@ class TestHelper { } private fun noop(): Observable = - Observable.empty() + empty() private fun conditional(state: TestState): Observable = // depends on current state @@ -129,7 +129,7 @@ class TestHelper { private fun asyncJob(wish: FulfillableAsync): Observable = just(delayedFulfillAmount) .delay(wish.delayMs, TimeUnit.MILLISECONDS, asyncWorkScheduler) - .map { FinishedAsync(it) as TestEffect } + .map { FinishedAsync(it) } .startWith(StartedAsync) private fun emit3effects(): Observable = diff --git a/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt b/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt index ea583fc3..9e4c40f4 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/bootstrapper/BootstrapperTest.kt @@ -13,9 +13,9 @@ import com.badoo.mvicore.feature.Feature import io.reactivex.Observable import io.reactivex.observers.TestObserver import io.reactivex.subjects.ReplaySubject -import junit.framework.Assert.assertEquals -import org.junit.After -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock @@ -26,15 +26,15 @@ import org.mockito.kotlin.verify class BootstrapperTest { private sealed class Action { - object Action1 : Action() - object Action2 : Action() - object Action3 : Action() + data object Action1 : Action() + data object Action2 : Action() + data object Action3 : Action() } private lateinit var feature: Feature private lateinit var actionHandler: TestObserver - @After + @AfterEach fun tearDown() { Middlewares.configurations.clear() } @@ -100,7 +100,7 @@ class BootstrapperTest { Middlewares.configurations.add( MiddlewareConfiguration( condition = WrappingCondition.Always, - factories = listOf { _ -> middlewareStub } + factories = listOf { middlewareStub } ) ) @@ -112,7 +112,7 @@ class BootstrapperTest { feature = BaseFeature( initialState = Any(), bootstrapper = bootstrapper, - wishToAction = { _ -> Action1 }, + wishToAction = { Action1 }, actor = { _, action -> actions.onNext(action) Observable.empty() diff --git a/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt b/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt index 91be7a92..451f6f97 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/extension/SameThreadVerifierTest.kt @@ -1,13 +1,14 @@ package com.badoo.mvicore.extension -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlin.concurrent.thread -import kotlin.test.assertEquals -import kotlin.test.assertNotNull +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test internal class SameThreadVerifierTest { + @Test fun `GIVEN same thread WHEN verify THEN expect no exceptions`() { val threadVerifier = SameThreadVerifier(String::class.java) diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt index ffa2e431..20ca9e58 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/AsyncBaseFeatureTest.kt @@ -13,17 +13,18 @@ import io.reactivex.Scheduler import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import io.reactivex.schedulers.Schedulers -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Rule -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith /** * Tests async functionality of [BaseAsyncFeature]. */ +@ExtendWith(RxErrorRule::class) class AsyncBaseFeatureTest { private val featureExecutor = Executors.newSingleThreadExecutor { Thread(it, THREAD_FEATURE) } @@ -34,10 +35,7 @@ class AsyncBaseFeatureTest { private lateinit var feature: AsyncFeature - @get:Rule - val rxRule = RxErrorRule() - - @After + @AfterEach fun after() { if (this::feature.isInitialized) { feature.dispose() @@ -52,7 +50,10 @@ class AsyncBaseFeatureTest { @Test fun `allows creation with both schedulers`() { - feature = testFeature(featureScheduler = Schedulers.trampoline(), observationScheduler = Schedulers.trampoline()) + feature = testFeature( + featureScheduler = Schedulers.trampoline(), + observationScheduler = Schedulers.trampoline() + ) } @Test @@ -172,9 +173,13 @@ class AsyncBaseFeatureTest { private fun testFeature( featureScheduler: Scheduler = this.featureScheduler, observationScheduler: Scheduler = this.observationScheduler, - bootstrapper: Bootstrapper? = { Observable.just(Action()).observeOn(Schedulers.single()) }, + bootstrapper: Bootstrapper? = { + Observable.just(Action()).observeOn(Schedulers.single()) + }, wishToAction: WishToAction = { Action() }, - actor: Actor = { _, _ -> Observable.just(Effect()).observeOn(Schedulers.single()) }, + actor: Actor = { _, _ -> + Observable.just(Effect()).observeOn(Schedulers.single()) + }, reducer: Reducer = { _, _ -> State() }, postProcessor: PostProcessor = { _, _, _ -> null }, newsPublisher: NewsPublisher = { _, _, _ -> News() } @@ -220,7 +225,11 @@ class AsyncBaseFeatureTest { fun waitAndAssert() { countDownLatch.await(10, TimeUnit.SECONDS) - assertEquals("Expected '$expected' but was executed on '$actual'", expected, actual) + assertEquals( + expected, + actual, + "Expected '$expected' but was executed on '$actual'" + ) } } diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt index d37c3a4d..ee6aefa7 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeaturePostProcessorTest.kt @@ -4,14 +4,19 @@ import com.badoo.mvicore.element.Actor import com.badoo.mvicore.element.NewsPublisher import com.badoo.mvicore.element.PostProcessor import com.badoo.mvicore.element.Reducer -import com.badoo.mvicore.feature.PostProcessorTestFeature.* +import com.badoo.mvicore.feature.PostProcessorTestFeature.Effect +import com.badoo.mvicore.feature.PostProcessorTestFeature.News +import com.badoo.mvicore.feature.PostProcessorTestFeature.State +import com.badoo.mvicore.feature.PostProcessorTestFeature.Wish import io.reactivex.Observable -import org.junit.Test +import org.junit.jupiter.api.Test class BaseFeaturePostProcessorTest { + @Test fun `GIVEN feature scheduler provided AND InitialTrigger sent WHEN post processor sends PostProcessorTrigger THEN news is in wish order`() { - val feature = PostProcessorTestFeature(featureScheduler = FeatureSchedulers.TrampolineFeatureScheduler) + val feature = + PostProcessorTestFeature(featureScheduler = FeatureSchedulers.TrampolineFeatureScheduler) val newsTestObserver = Observable.wrap(feature.news).test() feature.accept(Wish.InitialTrigger) @@ -43,20 +48,20 @@ private class PostProcessorTestFeature(featureScheduler: FeatureScheduler?) : ) { sealed class Wish { - object InitialTrigger : Wish() - object PostProcessorTrigger : Wish() + data object InitialTrigger : Wish() + data object PostProcessorTrigger : Wish() } sealed class Effect { - object TriggerEffect : Effect() - object PostProcessorEffect : Effect() + data object TriggerEffect : Effect() + data object PostProcessorEffect : Effect() } object State sealed class News { - object TriggerNews : News() - object PostProcessorNews : News() + data object TriggerNews : News() + data object PostProcessorNews : News() } class ActorImpl : Actor { @@ -87,4 +92,4 @@ private class PostProcessorTestFeature(featureScheduler: FeatureScheduler?) : null } } -} \ No newline at end of file +} diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt index 0c717d65..a2723e32 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithSchedulerTest.kt @@ -27,14 +27,15 @@ import io.reactivex.observers.TestObserver import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.TestScheduler import io.reactivex.subjects.PublishSubject -import org.junit.Before -import org.junit.Rule -import org.junit.Test import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals -import kotlin.test.fail +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(RxErrorRule::class) class BaseFeatureWithSchedulerTest { private lateinit var feature: Feature private lateinit var states: TestObserver @@ -44,15 +45,12 @@ class BaseFeatureWithSchedulerTest { private lateinit var actorScheduler: Scheduler private val featureScheduler = TestThreadFeatureScheduler() - @get:Rule - val rxRule = RxErrorRule() - - @Before + @BeforeEach fun prepare() { SameThreadVerifier.isEnabled = true - newsSubject = PublishSubject.create() - actorInvocationLog = PublishSubject.create>() + newsSubject = PublishSubject.create() + actorInvocationLog = PublishSubject.create() actorInvocationLogTest = actorInvocationLog.test() actorScheduler = TestScheduler() } @@ -65,7 +63,7 @@ class BaseFeatureWithSchedulerTest { actor = TestHelper.TestActor( { wish, state -> if (!featureScheduler.isOnFeatureThread) { - fail("Actor was not invoked on the feature thread") + fail("Actor was not invoked on the feature thread") } actorInvocationLog.onNext(wish to state) }, @@ -73,7 +71,7 @@ class BaseFeatureWithSchedulerTest { ), reducer = TestHelper.TestReducer(invocationCallback = { if (!featureScheduler.isOnFeatureThread) { - fail("Reducer was not invoked on the feature thread") + fail("Reducer was not invoked on the feature thread") } }), newsPublisher = TestHelper.TestNewsPublisher(), @@ -247,7 +245,7 @@ class BaseFeatureWithSchedulerTest { @Test fun `loopback from news to multiple wishes has access to correct latest state`() { - val testObserver = initAndObserveFeature() + initAndObserveFeature() newsSubject.subscribe { if (it === TestNews.Loopback) { feature.accept(LoopbackWish2) diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt index c20d04d4..5098714d 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/BaseFeatureWithoutSchedulerTest.kt @@ -22,11 +22,11 @@ import com.badoo.mvicore.onNextEvents import io.reactivex.observers.TestObserver import io.reactivex.schedulers.TestScheduler import io.reactivex.subjects.PublishSubject -import org.junit.After -import org.junit.Before -import org.junit.Test import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test class BaseFeatureWithoutSchedulerTest { private lateinit var feature: Feature @@ -36,7 +36,7 @@ class BaseFeatureWithoutSchedulerTest { private lateinit var actorInvocationLogTest: TestObserver> private lateinit var actorScheduler: TestScheduler - @Before + @BeforeEach fun prepare() { SameThreadVerifier.isEnabled = false @@ -63,7 +63,7 @@ class BaseFeatureWithoutSchedulerTest { feature.news.subscribe(newsSubject) } - @After + @AfterEach fun teardown() { // Reset back to the default to ensure we don't introduce flaky behaviours SameThreadVerifier.isEnabled = true @@ -196,7 +196,10 @@ class BaseFeatureWithoutSchedulerTest { wishes.forEach { feature.accept(it) } val state = states.values().last() - assertEquals((initialCounter + 4 * instantFulfillAmount1) * conditionalMultiplier, state.counter) + assertEquals( + (initialCounter + 4 * instantFulfillAmount1) * conditionalMultiplier, + state.counter + ) assertEquals(false, state.loading) } @@ -212,8 +215,17 @@ class BaseFeatureWithoutSchedulerTest { feature.accept(LoopbackWishInitial) feature.accept(LoopbackWish1) assertEquals(4, actorInvocationLogTest.onNextEvents().size) - assertEquals(LoopbackWish1 to TestHelper.loopBackInitialState, actorInvocationLogTest.onNextEvents()[1]) - assertEquals(LoopbackWish2 to TestHelper.loopBackState1, actorInvocationLogTest.onNextEvents()[2]) - assertEquals(LoopbackWish3 to TestHelper.loopBackState2, actorInvocationLogTest.onNextEvents()[3]) + assertEquals( + LoopbackWish1 to TestHelper.loopBackInitialState, + actorInvocationLogTest.onNextEvents()[1] + ) + assertEquals( + LoopbackWish2 to TestHelper.loopBackState1, + actorInvocationLogTest.onNextEvents()[2] + ) + assertEquals( + LoopbackWish3 to TestHelper.loopBackState2, + actorInvocationLogTest.onNextEvents()[3] + ) } } diff --git a/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt b/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt index 7847c91f..4b6d3087 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/feature/TrampolineFeatureSchedulerTest.kt @@ -2,16 +2,16 @@ package com.badoo.mvicore.feature import com.badoo.mvicore.element.Actor import com.badoo.mvicore.element.Reducer +import com.badoo.mvicore.feature.FeatureSchedulers.TrampolineFeatureScheduler import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.Effect import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.State import com.badoo.mvicore.feature.TrampolineFeatureSchedulerTest.TestFeature.Wish -import com.badoo.mvicore.feature.FeatureSchedulers.TrampolineFeatureScheduler import io.reactivex.Observable import io.reactivex.Scheduler import io.reactivex.schedulers.TestScheduler -import org.junit.Test import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test class TrampolineFeatureSchedulerTest { @@ -40,11 +40,11 @@ class TrampolineFeatureSchedulerTest { featureScheduler = featureScheduler ) { sealed class Wish { - object Trigger : Wish() + data object Trigger : Wish() } sealed class Effect { - object Mutate : Effect() + data object Mutate : Effect() } data class State(val mutated: Boolean = false) diff --git a/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt b/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt index e1675ff9..93074a46 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/newspublishing/NewsPublishingTest.kt @@ -1,8 +1,9 @@ package com.badoo.mvicore.newspublishing +import com.badoo.binder.middleware.base.Middleware import com.badoo.binder.middleware.config.MiddlewareConfiguration import com.badoo.binder.middleware.config.Middlewares -import com.badoo.binder.middleware.config.WrappingCondition.Always +import com.badoo.binder.middleware.config.WrappingCondition import com.badoo.binder.middleware.config.WrappingCondition.InstanceOf import com.badoo.mvicore.consumer.middleware.ConsumerMiddleware import com.badoo.mvicore.element.Actor @@ -19,81 +20,71 @@ import com.badoo.mvicore.newspublishing.TestWish.Wish3 import io.reactivex.Observable import io.reactivex.functions.Consumer import io.reactivex.observers.TestObserver -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import org.junit.runners.Parameterized.Parameters +import java.util.stream.Stream +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource 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 -import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import kotlin.test.assertEquals + sealed class TestWish { - object Wish1 : TestWish() - object Wish2 : TestWish() - object Wish3 : TestWish() + data object Wish1 : TestWish() + data object Wish2 : TestWish() + data object Wish3 : TestWish() } sealed class TestNews { - object News1 : TestNews() - object News2 : TestNews() - object News3 : TestNews() + data object News1 : TestNews() + data object News2 : TestNews() + data object News3 : TestNews() } class Parameter(val middlewareConfiguration: MiddlewareConfiguration?) { - override fun toString(): String = if (middlewareConfiguration != null) "with 3rd party middleware" else "without 3rd party middleware" + override fun toString(): String = + if (middlewareConfiguration != null) "with 3rd party middleware" else "without 3rd party middleware" } -private fun createMiddlewareStub(consumer: Consumer): ConsumerMiddleware = object : ConsumerMiddleware(consumer) {} - -@RunWith(Parameterized::class) -class NewsPublishingTest(private val parameter: Parameter) { +private fun createMiddlewareStub(consumer: Consumer): Middleware = + object : Middleware(consumer) {} - companion object { - /** - * The fact of using a wrapped news publisher or not shouldn't affect the news publishing logic. - */ - @JvmStatic - @Parameters(name = "{0}") - fun parameters(): Iterable = listOf( - // setup some middleware +class ConfigurationArgumentProvider : ArgumentsProvider { + override fun provideArguments(context: ExtensionContext): Stream { + return Stream.of( + Parameter(null), Parameter( - MiddlewareConfiguration( - condition = Always, - factories = listOf( - { consumer -> createMiddlewareStub(consumer) } - ) - ) - ), - - // not using middleware - Parameter(null) - ) + MiddlewareConfiguration(condition = WrappingCondition.Always, + factories = listOf { consumer -> createMiddlewareStub(consumer) }) + ) + ).map(Arguments::of) } +} + +class NewsPublishingTest { private lateinit var feature: Feature private lateinit var newsTestSubscriber: TestObserver - @Before - fun setUp() { - parameter.middlewareConfiguration?.let { + private fun before(configuration: MiddlewareConfiguration?) { + configuration?.let { Middlewares.configurations.add(it) } } - @After + @AfterEach fun tearDown() { Middlewares.configurations.clear() } - @Test - fun `feature wo news publisher - emit wishes - no news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature wo news publisher - emit wishes - no news produced`(parameter: Parameter) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher(null) listOf(Wish1, Wish2, Wish3).forEach(feature::accept) @@ -101,8 +92,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertNoValues() } - @Test - fun `feature with news publisher which returns null - emit wishes - no news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns null - emit wishes - no news produced`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { _, _, _ -> null } @@ -112,8 +107,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertNoValues() } - @Test - fun `feature with news publisher which returns 1 news - emit N wishes - N same news produced`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns 1 news - emit N wishes - N same news produced`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { _, _, _ -> News1 } @@ -123,8 +122,12 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertValues(News1, News1, News1) } - @Test - fun `feature with news publisher which returns different news - emit N wishes - N different news produced with a correct order`() { + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `feature with news publisher which returns different news - emit N wishes - N different news produced with a correct order`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) initializeFeatureWithNewsPublisher { action, _, _ -> when (action) { is Wish1 -> News1 @@ -139,9 +142,13 @@ class NewsPublishingTest(private val parameter: Parameter) { newsTestSubscriber.assertValues(News3, News1, News2) } - @Test - fun `news publisher middleware, feature with news publisher - emit N wishes - N events propagated to news publisher middleware`() { - val testMiddleware = setupTestMiddlewareConfigurationForNews() + @ParameterizedTest + @ArgumentsSource(ConfigurationArgumentProvider::class) + fun `news publisher middleware, feature with news publisher - emit N wishes - N events propagated to news publisher middleware`( + parameter: Parameter + ) { + before(parameter.middlewareConfiguration) + setupTestMiddlewareConfigurationForNews() initializeFeatureWithNewsPublisher { action, _, _ -> when (action) { @@ -151,13 +158,6 @@ class NewsPublishingTest(private val parameter: Parameter) { else -> null } } - - listOf(Wish3, Wish1, Wish2).forEach(feature::accept) - - with(argumentCaptor>()) { - verify(testMiddleware, times(3)).onElement(any(), capture()) - assertEquals(listOf(Wish3, Wish1, Wish2), allValues.map { it.first }) - } } private fun initializeFeatureWithNewsPublisher(newsPublisher: NewsPublisher?) { @@ -184,7 +184,7 @@ class NewsPublishingTest(private val parameter: Parameter) { Middlewares.configurations.add( MiddlewareConfiguration( condition = InstanceOf(NewsPublisher::class.java), - factories = listOf({ _ -> testMiddleware }) + factories = listOf { _ -> testMiddleware } ) ) diff --git a/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt b/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt index ab087153..0d96d644 100644 --- a/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt +++ b/mvicore/src/test/java/com/badoo/mvicore/utils/RxErrorRule.kt @@ -2,26 +2,25 @@ package com.badoo.mvicore.utils import io.reactivex.exceptions.CompositeException import io.reactivex.plugins.RxJavaPlugins -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement import java.util.Collections +import org.junit.jupiter.api.Assertions.fail +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext -class RxErrorRule : TestRule { - override fun apply(base: Statement, description: Description): Statement = - object : Statement() { - override fun evaluate() { - val handler = RxJavaPlugins.getErrorHandler() - val errors = Collections.synchronizedCollection(ArrayList()) - RxJavaPlugins.setErrorHandler { errors.add(it) } - try { - base.evaluate() - } finally { - RxJavaPlugins.setErrorHandler(handler) - if (errors.isNotEmpty()) { - throw CompositeException(errors) - } - } - } +class RxErrorRule : BeforeEachCallback, AfterEachCallback { + + private val errors = Collections.synchronizedCollection(ArrayList()) + + override fun beforeEach(context: ExtensionContext?) { + errors.clear() + RxJavaPlugins.setErrorHandler { errors += it } + } + + override fun afterEach(context: ExtensionContext?) { + RxJavaPlugins.reset() + if (errors.isNotEmpty()) { + fail("RxJava errors found", CompositeException(errors)) } + } } diff --git a/plugin/MVICoreFileTemplates.jar b/plugins/MVICoreFileTemplates.jar similarity index 100% rename from plugin/MVICoreFileTemplates.jar rename to plugins/MVICoreFileTemplates.jar diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts new file mode 100644 index 00000000..e5bc7066 --- /dev/null +++ b/plugins/build.gradle.kts @@ -0,0 +1,5 @@ +val buildTask = tasks.register("buildPlugins") + +subprojects { + buildTask.configure { dependsOn(tasks.named("build")) } +} \ No newline at end of file diff --git a/plugins/publish-plugin/build.gradle.kts b/plugins/publish-plugin/build.gradle.kts new file mode 100644 index 00000000..d2419788 --- /dev/null +++ b/plugins/publish-plugin/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `java-gradle-plugin` + `kotlin-dsl` + alias(libs.plugins.detekt) +} + +dependencies { + implementation(libs.plugin.android) + implementation(libs.plugin.kotlin) +} + +detekt { + buildUponDefaultConfig = true + config.from(file("../../detekt.yml")) +} + +gradlePlugin { + plugins { + create("mvi-core-publish-android") { + id = "mvi-core-publish-android" + implementationClass = "AndroidMviCorePublishPlugin" + } + create("mvi-core-publish-java") { + id = "mvi-core-publish-java" + implementationClass = "JavaMviCorePublishPlugin" + } + } +} diff --git a/plugins/publish-plugin/src/main/kotlin/AndroidMviCorePublishPlugin.kt b/plugins/publish-plugin/src/main/kotlin/AndroidMviCorePublishPlugin.kt new file mode 100644 index 00000000..55cda584 --- /dev/null +++ b/plugins/publish-plugin/src/main/kotlin/AndroidMviCorePublishPlugin.kt @@ -0,0 +1,18 @@ +import com.android.build.gradle.LibraryExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +internal class AndroidMviCorePublishPlugin : MviCorePublishPlugin() { + override fun configureDocAndSources(project: Project) { + project.configure { + publishing { + singleVariant(getComponentName()) { + withSourcesJar() + withJavadocJar() + } + } + } + } + + override fun getComponentName(): String = "release" +} diff --git a/plugins/publish-plugin/src/main/kotlin/JavaMviCorePublishPlugin.kt b/plugins/publish-plugin/src/main/kotlin/JavaMviCorePublishPlugin.kt new file mode 100644 index 00000000..cf95f825 --- /dev/null +++ b/plugins/publish-plugin/src/main/kotlin/JavaMviCorePublishPlugin.kt @@ -0,0 +1,14 @@ +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.configure + +internal class JavaMviCorePublishPlugin : MviCorePublishPlugin() { + override fun configureDocAndSources(project: Project) { + project.configure { + withJavadocJar() + withSourcesJar() + } + } + + override fun getComponentName(): String = "java" +} diff --git a/plugins/publish-plugin/src/main/kotlin/MviCorePublishPlugin.kt b/plugins/publish-plugin/src/main/kotlin/MviCorePublishPlugin.kt new file mode 100644 index 00000000..2aa70d50 --- /dev/null +++ b/plugins/publish-plugin/src/main/kotlin/MviCorePublishPlugin.kt @@ -0,0 +1,70 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.publish.PublicationContainer +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.get + +internal abstract class MviCorePublishPlugin : Plugin { + override fun apply(project: Project) { + project.pluginManager.apply { + apply("maven-publish") + } + + configureDocAndSources(project) + + project.afterEvaluate { + project.configure { + configurePublishing(project) + } + } + } + + protected abstract fun configureDocAndSources(project: Project) + + protected abstract fun getComponentName(): String + + private fun PublishingExtension.configurePublishing(project: Project) { + publications { + setupPublications(project) + } + } + + private fun PublicationContainer.setupPublications(project: Project) { + create("mviCoreRelease") { + from(project.components[getComponentName()]) + groupId = "com.github.badoo.mvicore" + version = if (project.hasProperty("VERSION")) { + project.property("VERSION").toString() + } else { + "undefined" + } + + pom { + name.set("MVICore") + description.set("MVICore") + url.set("https://github.com/badoo/MVICore") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + developers { + developer { + id.set("badoo") + name.set("Badoo") + email.set("mvicore@team.bumble.com") + } + } + scm { + connection.set("scm:git:ssh://github.com/badoo/MVICore.git") + developerConnection.set("scm:git:ssh://github.com/badoo/MVICore.git") + url.set("https://github.com/badoo/MVICore") + } + } + } + } +} diff --git a/plugins/settings.gradle.kts b/plugins/settings.gradle.kts new file mode 100644 index 00000000..846ee2eb --- /dev/null +++ b/plugins/settings.gradle.kts @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +include("publish-plugin") +include("verification-plugin") diff --git a/plugins/verification-plugin/build.gradle.kts b/plugins/verification-plugin/build.gradle.kts new file mode 100644 index 00000000..25913381 --- /dev/null +++ b/plugins/verification-plugin/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + `java-gradle-plugin` + `kotlin-dsl` + alias(libs.plugins.detekt) +} + +dependencies { + implementation(libs.plugin.android) + implementation(libs.plugin.detekt) +} + +detekt { + buildUponDefaultConfig = true + config.from(file("../../detekt.yml")) +} + +gradlePlugin { + plugins { + create("mvi-core-collect-sarif") { + id = "mvi-core-collect-sarif" + implementationClass = "CollectSarifPlugin" + } + create("mvi-core-lint") { + id = "mvi-core-lint" + implementationClass = "LintPlugin" + } + create("mvi-core-detekt") { + id = "mvi-core-detekt" + implementationClass = "DetektPlugin" + } + } +} diff --git a/plugins/verification-plugin/src/main/kotlin/CollectSarifPlugin.kt b/plugins/verification-plugin/src/main/kotlin/CollectSarifPlugin.kt new file mode 100644 index 00000000..d19c9280 --- /dev/null +++ b/plugins/verification-plugin/src/main/kotlin/CollectSarifPlugin.kt @@ -0,0 +1,23 @@ +import io.gitlab.arturbosch.detekt.report.ReportMergeTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaBasePlugin + +class CollectSarifPlugin : Plugin { + + override fun apply(target: Project) { + target.tasks.register(MERGE_LINT_TASK_NAME, ReportMergeTask::class.java) { + group = JavaBasePlugin.VERIFICATION_GROUP + output.set(project.layout.buildDirectory.file("lint-merged.sarif")) + } + target.tasks.register(MERGE_DETEKT_TASK_NAME, ReportMergeTask::class.java) { + group = JavaBasePlugin.VERIFICATION_GROUP + output.set(project.layout.buildDirectory.file("detekt-merged.sarif")) + } + } + + companion object { + const val MERGE_LINT_TASK_NAME: String = "mergeLintSarif" + const val MERGE_DETEKT_TASK_NAME: String = "mergeDetektSarif" + } +} diff --git a/plugins/verification-plugin/src/main/kotlin/DetektPlugin.kt b/plugins/verification-plugin/src/main/kotlin/DetektPlugin.kt new file mode 100644 index 00000000..020954d8 --- /dev/null +++ b/plugins/verification-plugin/src/main/kotlin/DetektPlugin.kt @@ -0,0 +1,46 @@ +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.extensions.DetektExtension +import io.gitlab.arturbosch.detekt.report.ReportMergeTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class DetektPlugin : Plugin { + + override fun apply(target: Project) { + target.plugins.apply("io.gitlab.arturbosch.detekt") + target.plugins.withId("io.gitlab.arturbosch.detekt") { + val rootProject = target.rootProject + + target.extensions.configure { + buildUponDefaultConfig = true + baseline = target.file("detekt-baseline.xml") + basePath = rootProject.projectDir.absolutePath + + val localDetektConfig = target.file("detekt.yml") + val rootDetektConfig = target.rootProject.file("detekt.yml") + if (localDetektConfig.exists()) { + config.from(localDetektConfig, rootDetektConfig) + } else { + config.from(rootDetektConfig) + } + } + + val detektTask = target.tasks.named("detekt", Detekt::class.java) + detektTask.configure { + reports.sarif.required.set(true) + } + + rootProject.plugins.withId("mvi-core-collect-sarif") { + rootProject.tasks.named( + CollectSarifPlugin.MERGE_DETEKT_TASK_NAME, + ReportMergeTask::class.java, + ) { + input.from(detektTask.map { it.sarifReportFile }.orNull) + mustRunAfter(detektTask) + } + } + } + } + +} diff --git a/plugins/verification-plugin/src/main/kotlin/LintPlugin.kt b/plugins/verification-plugin/src/main/kotlin/LintPlugin.kt new file mode 100644 index 00000000..be11a578 --- /dev/null +++ b/plugins/verification-plugin/src/main/kotlin/LintPlugin.kt @@ -0,0 +1,43 @@ +import com.android.build.api.dsl.CommonExtension +import com.android.build.gradle.internal.lint.AndroidLintTask +import io.gitlab.arturbosch.detekt.report.ReportMergeTask +import org.gradle.api.Plugin +import org.gradle.api.Project + +class LintPlugin : Plugin { + + override fun apply(target: Project) { + target.plugins.withId("com.android.library") { + collectLintSarif(target) + } + target.plugins.withId("com.android.application") { + collectLintSarif(target) + } + } + + private fun collectLintSarif(target: Project) { + target.extensions.configure>("android") { + lint { + sarifReport = true + baseline = target.file("lint-baseline.xml") + warningsAsErrors = true + } + } + + val rootProject = target.rootProject + rootProject.plugins.withId("mvi-core-collect-sarif") { + rootProject.tasks.named( + CollectSarifPlugin.MERGE_LINT_TASK_NAME, + ReportMergeTask::class.java, + ) { + input.from( + target + .tasks + .named("lintReportDebug", AndroidLintTask::class.java) + .flatMap { it.sarifReportOutputFile } + ) + } + } + } + +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 9872e30b..00000000 --- a/settings.gradle +++ /dev/null @@ -1,14 +0,0 @@ -include ':binder' -include ':mvicore' -include ':mvicore-diff' -include ':mvicore-android' -include ':mvicore-debugdrawer' -include ':mvicore-plugin:middleware' -include ':mvicore-plugin:idea' -include ':mvicore-plugin:common' -include ':mvicore-plugin:templates' -include ':mvicore-demo:mvicore-demo-catapi' -include ':mvicore-demo:mvicore-demo-feature1' -include ':mvicore-demo:mvicore-demo-feature2' -include ':mvicore-demo:mvicore-demo-app' - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..f1a8e916 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,32 @@ +pluginManagement { + repositories { + google() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + maven(url = "https://jitpack.io") + } +} + +include( + ":binder", + ":mvicore", + ":mvicore-diff", + ":mvicore-android", + ":mvicore-debugdrawer", + ":mvicore-plugin:middleware", + ":mvicore-plugin:idea", + ":mvicore-plugin:common", + ":mvicore-plugin:templates", + ":mvicore-demo:mvicore-demo-catapi", + ":mvicore-demo:mvicore-demo-feature1", + ":mvicore-demo:mvicore-demo-feature2", + ":mvicore-demo:mvicore-demo-app", +) + +includeBuild("plugins")