diff --git a/build.gradle.kts b/build.gradle.kts index d8641027..3e04c554 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,14 @@ plugins { id("jacoco") - alias(libraries.plugins.android.application) apply false - alias(libraries.plugins.android.library) apply false - alias(libraries.plugins.google.ksp) apply false - alias(libraries.plugins.jetbrains.kotlin) apply false - alias(libraries.plugins.jetbrains.multiplatform) apply false - alias(libraries.plugins.jetbrains.serialization) apply false - alias(libraries.plugins.dexcount) apply false - alias(libraries.plugins.detekt) apply false + alias(pluginLibraries.plugins.android.application) apply false + alias(pluginLibraries.plugins.android.library) apply false + alias(pluginLibraries.plugins.google.ksp) apply false + alias(pluginLibraries.plugins.jetbrains.kotlin) apply false + alias(pluginLibraries.plugins.jetbrains.multiplatform) apply false + alias(pluginLibraries.plugins.jetbrains.serialization) apply false + alias(pluginLibraries.plugins.dexcount) apply false + alias(pluginLibraries.plugins.detekt) apply false // TODO Pedrinho, help me -// alias(libraries.plugins.pedrinho_publish) apply false +// alias(pluginLibraries.plugins.pedrinho_publish) apply false } diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt b/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt index bdf63680..812b9bb2 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/_common.kt @@ -3,7 +3,7 @@ package com.toolkit.plugin import com.android.build.api.dsl.CommonExtension import com.toolkit.plugin.util.projectJavaVersion -internal fun CommonExtension<*, *, *, *, *>.commonSetup() { +internal fun CommonExtension<*, *, *, *, *, *>.commonSetup() { androidResources { noCompress.add("") } compileOptions { @@ -18,7 +18,7 @@ internal fun CommonExtension<*, *, *, *, *>.commonSetup() { } } -internal fun CommonExtension<*, *, *, *, *>.regularSourceSets() { +internal fun CommonExtension<*, *, *, *, *, *>.regularSourceSets() { sourceSets { maybeCreate("main").java.srcDirs("src/main/kotlin") maybeCreate("test").java.srcDirs("src/test/kotlin") diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt b/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt index d6cbfd64..be3047c9 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/multiplatform/ToolkitBasePlugin.kt @@ -5,7 +5,6 @@ package com.toolkit.plugin.multiplatform import com.toolkit.plugin.util.applyPlugins import com.toolkit.plugin.util.multiplatform import com.toolkit.plugin.util.projectJavaVersionCode -import com.toolkit.plugin.util.projectJavaVersionName import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin @@ -22,13 +21,7 @@ internal class ToolkitBasePlugin : Plugin { } with(target.multiplatform) { - androidTarget { - compilations.all { compilation -> - compilation.kotlinOptions { - jvmTarget = projectJavaVersionName - } - } - } + androidTarget {} } target.plugins.apply("toolkit-optimize") diff --git a/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt b/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt index 153f288c..efa2ccc2 100644 --- a/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt +++ b/plugin/src/main/kotlin/com/toolkit/plugin/util/_projectExtensions.kt @@ -24,6 +24,12 @@ internal val Project.libraries: VersionCatalog return extensions.findByType(VersionCatalogsExtension::class.java)?.named("libraries") ?: error("Cannot find libraries in version catalog!") } +internal val Project.pluginLibraries: VersionCatalog + @Throws(IllegalStateException::class) + get() { + return extensions.findByType(VersionCatalogsExtension::class.java)?.named("pluginLibraries") + ?: error("Cannot find libraries in version catalog!") + } internal val Project.ktLint: KtlintExtension @Throws(IllegalStateException::class) @@ -97,6 +103,6 @@ internal val Project.sign: SigningExtension internal fun Project.applyPlugins(vararg id: String) { id.forEach { - plugins.apply(libraries.findPlugin(it).get().get().pluginId) + plugins.apply(pluginLibraries.findPlugin(it).get().get().pluginId) } } diff --git a/samples/github-list-project/build.gradle.kts b/samples/github-list-project/build.gradle.kts index b2cac8e9..75721e9f 100644 --- a/samples/github-list-project/build.gradle.kts +++ b/samples/github-list-project/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("toolkit-android-sample") // id("toolkit-compose") --> Commented until Compose finally work on API 34 -.-" - alias(libraries.plugins.google.ksp) + alias(pluginLibraries.plugins.google.ksp) } android.namespace = "br.com.arch.toolkit.sample.github" diff --git a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt index b521419c..2f385184 100644 --- a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt +++ b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/item/RepositoryItemView.kt @@ -2,7 +2,7 @@ package br.com.arch.toolkit.sample.github.ui.xml.item import android.content.Context import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.github.data.remote.model.RepoDTO class RepositoryItemView(context: Context) : AppCompatTextView(context), ViewBinder { diff --git a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt index 85384f79..60c77c2d 100644 --- a/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt +++ b/samples/github-list-project/src/main/kotlin/br/com/arch/toolkit/sample/github/ui/xml/list/withoutPagination/RepositoryListActivity.kt @@ -7,7 +7,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewModelProvider import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleAdapter +import br.com.arch.toolkit.recyclerAdapter.SimpleAdapter import br.com.arch.toolkit.result.DataResultStatus import br.com.arch.toolkit.sample.github.R import br.com.arch.toolkit.sample.github.data.remote.model.RepoDTO diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt index 5e29f6d7..4c9a4566 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/SimpleListActivity.kt @@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleAdapter +import br.com.arch.toolkit.recyclerAdapter.SimpleAdapter import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.SimpleItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt index e4449e64..8b8af0d4 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/StickyHeadersActivity.kt @@ -6,9 +6,9 @@ import android.widget.Toast import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.RecyclerView import br.com.arch.toolkit.delegate.viewProvider -import br.com.arch.toolkit.playground.recyclerAdapter.SimpleStickyAdapter -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaderModel -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeadersLinearLayoutManager +import br.com.arch.toolkit.recyclerAdapter.SimpleStickyAdapter +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaderModel +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeadersLinearLayoutManager import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.StickyItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt index 0ca2b79c..f1e1546e 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/adapter/MultipleViewTypesAdapter.kt @@ -1,8 +1,8 @@ package br.com.arch.toolkit.sample.playground.recyclerAdapter.adapter import android.content.Context -import br.com.arch.toolkit.playground.recyclerAdapter.BaseRecyclerAdapter -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.BaseRecyclerAdapter +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.AnotherSimpleItemView import br.com.arch.toolkit.sample.playground.recyclerAdapter.itemView.SimpleItemView diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt index a04041ca..f5d109ee 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/AnotherSimpleItemView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R class AnotherSimpleItemView : AppCompatTextView, ViewBinder { diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt index 229a600b..eaaad7be 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/SimpleItemView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R class SimpleItemView : AppCompatTextView, ViewBinder { diff --git a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt index 3b3ae2d3..975d74e4 100644 --- a/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt +++ b/samples/playground-android/src/main/kotlin/br/com/arch/toolkit/sample/playground/recyclerAdapter/itemView/StickyItemView.kt @@ -4,7 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.view.ViewGroup import androidx.appcompat.widget.AppCompatTextView -import br.com.arch.toolkit.playground.recyclerAdapter.ViewBinder +import br.com.arch.toolkit.recyclerAdapter.ViewBinder import br.com.arch.toolkit.sample.playground.R import br.com.arch.toolkit.sample.playground.recyclerAdapter.StickyHeadersActivity diff --git a/samples/playground/build.gradle.kts b/samples/playground/build.gradle.kts index b8682a4b..27a12ffc 100644 --- a/samples/playground/build.gradle.kts +++ b/samples/playground/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("toolkit-android-sample") - alias(libraries.plugins.jetbrains.serialization) + alias(pluginLibraries.plugins.jetbrains.serialization) } android.namespace = "br.com.arch.toolkit.sample.livedata" diff --git a/settings.gradle.kts b/settings.gradle.kts index 46246a7f..6f35ef40 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,6 +16,9 @@ dependencyResolutionManagement { register("libraries") { from(files("$rootDir/tools/libs.versions.toml")) } + register("pluginLibraries") { + from(files("$rootDir/tools/plugin.versions.toml")) + } } } diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt index fdc9aaac..d391d412 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/livedata/ResponseLiveData.kt @@ -1,4 +1,10 @@ -@file:Suppress("KotlinNullnessAnnotation", "TooManyFunctions") +@file:Suppress( + "KotlinNullnessAnnotation", + "TooManyFunctions", + "MemberVisibilityCanBePrivate", + "DeprecatedCallableAddReplaceWith", + "unused" +) package br.com.arch.toolkit.livedata @@ -126,9 +132,7 @@ open class ResponseLiveData : LiveData> { * @see ResponseLiveData.onError */ @NonNull - fun mapError( - @NonNull transformation: (Throwable) -> Throwable - ): ResponseLiveData { + fun mapError(@NonNull transformation: (Throwable) -> Throwable): ResponseLiveData { val liveData = SwapResponseLiveData() .scope(scope) .transformDispatcher(transformDispatcher) @@ -153,9 +157,7 @@ open class ResponseLiveData : LiveData> { * @see ResponseLiveData.onErrorReturn */ @NonNull - fun onErrorReturn( - @NonNull onErrorReturn: ((Throwable) -> T) - ): ResponseLiveData { + fun onErrorReturn(@NonNull onErrorReturn: ((Throwable) -> T)): ResponseLiveData { val liveData = SwapResponseLiveData() .scope(scope) .transformDispatcher(transformDispatcher) @@ -172,6 +174,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use combine instead") fun mergeWith(@NonNull source: ResponseLiveData): ResponseLiveData> = withDelegate { merge(this@ResponseLiveData, source, scope, transformDispatcher) @@ -187,6 +190,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use combine instead") fun mergeWith( @NonNull tag: String, @NonNull vararg sources: Pair> @@ -209,6 +213,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use chainWith instead") fun followedBy( @NonNull source: (T) -> ResponseLiveData, @NonNull condition: (T) -> Boolean, @@ -235,6 +240,7 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental + @Deprecated("Use chainWith instead") fun followedBy( @NonNull source: (T) -> ResponseLiveData, @NonNull condition: (T) -> Boolean @@ -250,14 +256,8 @@ open class ResponseLiveData : LiveData> { */ @NonNull @Experimental - fun followedBy( - @NonNull source: (T) -> ResponseLiveData - ): ResponseLiveData> = followedBy(source) { true } - //endregion - - //region Operators - @Experimental - operator fun plus(source: ResponseLiveData) = mergeWith(source) + @Deprecated("Use chainWith instead") + fun followedBy(@NonNull source: (T) -> ResponseLiveData) = followedBy(source) { true } //endregion //region Observability diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt index 6081535a..86762081 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_chain.kt @@ -1,4 +1,4 @@ -@file:Suppress("Filename", "TooManyFunctions", "LongParameterList") +@file:Suppress("Filename", "TooManyFunctions", "LongParameterList", "unused") package br.com.arch.toolkit.util @@ -47,13 +47,15 @@ fun LiveData.chainWith( val mediator = MediatorLiveData>() var bLiveData: LiveData? = null + fun onBReceived(bValue: R?) = (value to bValue).let(mediator::setValue) + fun onAReceived(aValue: T?) { bLiveData?.let(mediator::removeSource) val isConditionMet = condition.runCatching { invoke(aValue) }.getOrDefault(false) bLiveData = if (isConditionMet) other.runCatching { invoke(aValue) }.getOrNull() else null bLiveData?.let { liveData -> if (liveData.isInitialized.not()) mediator.value = aValue to null - mediator.addSource(liveData) { bValue -> (aValue to bValue).let(mediator::setValue) } + mediator.addSource(liveData, ::onBReceived) } } @@ -83,10 +85,23 @@ fun LiveData.chainWith( fun LiveData.chainNotNullWith( other: (T) -> LiveData, condition: (T) -> Boolean -): LiveData> = chainWith( - other = { it?.let(other) ?: error("Data null in chainNotNullWith") }, - condition = { it?.let(condition) ?: false } -).mapNotNull { it.toNotNull() } +): LiveData> { + val mediator = MediatorLiveData>() + var bLiveData: LiveData? = null + + fun onBReceived(bValue: R?) = (value to bValue).onlyWithValues()?.let(mediator::setValue) + + fun onAReceived(aValue: T?) { + bLiveData?.let(mediator::removeSource) + aValue ?: return + val isConditionMet = condition.runCatching { invoke(aValue) }.getOrDefault(false) + bLiveData = if (isConditionMet) other.runCatching { invoke(aValue) }.getOrNull() else null + bLiveData?.let { liveData -> mediator.addSource(liveData, ::onBReceived) } + } + + mediator.addSource(this, ::onAReceived) + return mediator +} /* Coroutine Functions -------------------------------------------------------------------------- */ @@ -115,7 +130,7 @@ fun LiveData.chainWith( context: CoroutineContext, other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, -) = liveData(context) { internalChainWith(other, condition).collect(::emit) } +): LiveData> = liveData(context) { internalChainWith(other, condition).collect(::emit) } /** * Chains this [LiveData] with another [LiveData] based on a condition, using a transformation function. @@ -184,7 +199,12 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: suspend (T?, R?) -> X? -): LiveData = chainWith(context, other, condition, Dispatchers.IO to transform) +): LiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this [LiveData] with another [LiveData] based on a condition, using a simple transformation function and the default [CoroutineContext]. @@ -212,7 +232,12 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: Pair X?> -): LiveData = chainWith(EmptyCoroutineContext, other, condition, transform) +): LiveData = chainWith( + context = EmptyCoroutineContext, + other = other, + condition = condition, + transform = transform +) /** * Chains this [LiveData] with another [LiveData] based on a condition, @@ -241,7 +266,11 @@ fun LiveData.chainWith( other: suspend (T?) -> LiveData, condition: suspend (T?) -> Boolean, transform: suspend (T?, R?) -> X? -): LiveData = chainWith(other, condition, Dispatchers.IO to transform) +): LiveData = chainWith( + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this [LiveData] with another non-nullable [LiveData] based on a condition, using coroutines. @@ -268,7 +297,7 @@ fun LiveData.chainNotNullWith( context: CoroutineContext, other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, -) = liveData>(context) { +): LiveData> = liveData(context) { internalChainNotNullWith(other, condition).collect(::emit) } @@ -339,7 +368,12 @@ fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: suspend (T, R) -> X -): LiveData = chainNotNullWith(context, other, condition, Dispatchers.IO to transform) +): LiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /** * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, @@ -368,10 +402,16 @@ fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: Pair X> -): LiveData = chainNotNullWith(EmptyCoroutineContext, other, condition, transform) +): LiveData = chainNotNullWith( + context = EmptyCoroutineContext, + other = other, + condition = condition, + transform = transform +) /** - * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, using a simple transformation function and the default [CoroutineContext] and [CoroutineDispatcher]. + * Chains this non-nullable [LiveData] with another non-nullable [LiveData] based on a condition, + * using a simple transformation function and the default [CoroutineContext] and [CoroutineDispatcher]. * * @param other A suspend function that returns another non-nullable [LiveData] based on the value of this [LiveData]. * @param condition A suspend function that determines whether to chain with the other [LiveData] based on a non-nullable value. @@ -391,23 +431,28 @@ fun LiveData.chainNotNullWith( * transform = { a, b -> "$a$b" } * ) * ``` - */fun LiveData.chainNotNullWith( + */ +fun LiveData.chainNotNullWith( other: suspend (T) -> LiveData, condition: suspend (T) -> Boolean, transform: suspend (T, R) -> X -): LiveData = chainNotNullWith(other, condition, Dispatchers.IO to transform) +): LiveData = chainNotNullWith( + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) /* Auxiliary Functions -------------------------------------------------------------------------- */ -private suspend inline fun LiveData.internalChainNotNullWith( +internal suspend inline fun LiveData.internalChainNotNullWith( noinline other: suspend (T) -> LiveData, noinline condition: suspend (T) -> Boolean, ) = internalChainWith( other = { data -> data?.let { other(it) } ?: error("Data null in chainNotNullWith") }, condition = { data -> data?.let { condition(it) } ?: false } -).mapNotNull { it.toNotNull() } +).mapNotNull { it.onlyWithValues() } -private suspend inline fun LiveData.internalChainWith( +internal suspend inline fun LiveData.internalChainWith( noinline other: suspend (T?) -> LiveData, noinline condition: suspend (T?) -> Boolean, ) = channelFlow { diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt index a8d4bb51..3b901d95 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_combine.kt @@ -44,28 +44,6 @@ import kotlin.coroutines.EmptyCoroutineContext */ operator fun LiveData.plus(other: LiveData) = combine(other = other) -/** - * Combines two [LiveData] objects using the `+` operator with a specified [CoroutineContext]. - * - * This method allows you to merge two [LiveData] sources into a single [LiveData] while specifying the context in which the operation should occur. - * - * @param context The [CoroutineContext] in which to perform the combination. - * @param other The other [LiveData] to combine with. - * @return A [LiveData] that emits pairs of values from both [LiveData] sources. - * - * ### Example Usage: - * ``` - * val liveData1: LiveData = MutableLiveData(1) - * val liveData2: LiveData = MutableLiveData("A") - * val combinedLiveData: LiveData> = liveData1.plus(Dispatchers.IO, liveData2) - * ``` - * - * @see [combine] - * @see [plus] - */ -fun LiveData.plus(context: CoroutineContext, other: LiveData) = - combine(context = context, other = other) - /* Regular Functions ---------------------------------------------------------------------------- */ /** @@ -130,18 +108,18 @@ fun LiveData.combineNotNull(other: LiveData): LiveData> val initial = (value to other.value).takeIf { isInitialized || other.isInitialized } val mediator = when { initial == null -> MediatorLiveData() - initial.toNotNull() == null -> MediatorLiveData() - else -> MediatorLiveData>(initial.toNotNull()) + initial.onlyWithValues() == null -> MediatorLiveData() + else -> MediatorLiveData>(initial.onlyWithValues()) } mediator.addSource(this) { (it to other.value).takeUnless { ignoreA.compareAndSet(true, false) } - ?.toNotNull() + ?.onlyWithValues() ?.let(mediator::setValue) } mediator.addSource(other) { (value to it).takeUnless { ignoreB.compareAndSet(true, false) } - ?.toNotNull() + ?.onlyWithValues() ?.let(mediator::setValue) } return mediator @@ -168,8 +146,10 @@ fun LiveData.combineNotNull(other: LiveData): LiveData> * @see [combineNotNull] * @see [plus] */ -fun LiveData.combine(context: CoroutineContext, other: LiveData) = - liveData(context) { internalCombine(other).collect(::emit) } +fun LiveData.combine( + context: CoroutineContext, + other: LiveData +): LiveData> = liveData(context) { internalCombine(other).collect(::emit) } /** * Combines two [LiveData] objects with a transformation function using coroutines. @@ -236,7 +216,11 @@ fun LiveData.combine( context: CoroutineContext, other: LiveData, transform: suspend (T?, R?) -> X? -): LiveData = combine(context, other, Dispatchers.IO to transform) +): LiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two [LiveData] objects with a transformation function and an optional [CoroutineContext]. @@ -262,7 +246,11 @@ fun LiveData.combine( fun LiveData.combine( other: LiveData, transform: Pair X?> -): LiveData = combine(EmptyCoroutineContext, other, transform) +): LiveData = combine( + context = EmptyCoroutineContext, + other = other, + transform = transform +) /** * Combines two [LiveData] objects with a transformation function using a default [CoroutineDispatcher]. @@ -287,7 +275,10 @@ fun LiveData.combine( fun LiveData.combine( other: LiveData, transform: suspend (T?, R?) -> X? -): LiveData = combine(other, Dispatchers.IO to transform) +): LiveData = combine( + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two non-null [LiveData] objects using coroutines. @@ -308,8 +299,10 @@ fun LiveData.combine( * @see [combine] * @see [plus] */ -fun LiveData.combineNotNull(context: CoroutineContext, other: LiveData) = - liveData>(context) { internalCombineNotNull(other).collect(::emit) } +fun LiveData.combineNotNull( + context: CoroutineContext, + other: LiveData +): LiveData> = liveData(context) { internalCombineNotNull(other).collect(::emit) } /** * Combines two non-null [LiveData] objects with a transformation function using coroutines. @@ -386,7 +379,11 @@ fun LiveData.combineNotNull( context: CoroutineContext, other: LiveData, transform: suspend (T, R) -> X -): LiveData = combineNotNull(context, other, Dispatchers.IO to transform) +): LiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) /** * Combines two non-null [LiveData] objects with a transformation function and an optional [CoroutineContext]. @@ -412,7 +409,11 @@ fun LiveData.combineNotNull( fun LiveData.combineNotNull( other: LiveData, transform: Pair X> -): LiveData = combineNotNull(EmptyCoroutineContext, other, transform) +): LiveData = combineNotNull( + context = EmptyCoroutineContext, + other = other, + transform = transform +) /** * Combines two non-null [LiveData] objects with a transformation function using a default [CoroutineDispatcher]. @@ -437,12 +438,15 @@ fun LiveData.combineNotNull( fun LiveData.combineNotNull( other: LiveData, transform: suspend (T, R) -> X -): LiveData = combineNotNull(other, Dispatchers.IO to transform) +): LiveData = combineNotNull( + other = other, + transform = Dispatchers.IO to transform +) /* Auxiliary Functions -------------------------------------------------------------------------- */ internal suspend inline fun LiveData.internalCombineNotNull(other: LiveData) = - internalCombine(other).mapNotNull { it.toNotNull() } + internalCombine(other).mapNotNull { it.onlyWithValues() } internal suspend inline fun LiveData.internalCombine(other: LiveData) = channelFlow { val aFlow = this@internalCombine.asFlow() diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt index 9146ceed..a788a683 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_dataResult.kt @@ -109,7 +109,7 @@ fun dataResultNone() = DataResult(null, null, NONE) * * @return A [DataResult] with a pair of non-null values, or null if any value is null. */ -fun DataResult>.toNotNull(): DataResult>? = when (val data = data) { +fun DataResult>.onlyWithValues(): DataResult>? = when (val data = data) { null -> DataResult(null, error, status) else -> data.runCatching { requireNotNull(first) to requireNotNull(second) } .mapCatching { DataResult>(it, error, status) } @@ -215,9 +215,7 @@ fun DataResult?.merge(second: DataResult?): DataResult fun DataResult?.mergeNotNull(second: DataResult?): DataResult> { val mergeNullable = merge(second) - val data = mergeNullable.data?.runCatching { - requireNotNull(this.first) to requireNotNull(this.second) - }?.getOrNull() + val data = mergeNullable.data?.onlyWithValues() val error = mergeNullable.error val status = mergeNullable.status diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt index 47b3b369..595fcbf4 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_liveData.kt @@ -1,4 +1,5 @@ @file:JvmName("LiveDataUtils") +@file:Suppress("unused") package br.com.arch.toolkit.util diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt index 10eca318..f01a6fe7 100644 --- a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_pair.kt @@ -13,15 +13,15 @@ package br.com.arch.toolkit.util * Example usage: * ``` * val pair: Pair = "Hello" to 42 - * val nonNullPair: Pair? = pair.toNotNull() + * val nonNullPair: Pair? = pair.onlyWithValues() * nonNullPair?.let { (first, second) -> * println("First: $first, Second: $second") // Output: First: Hello, Second: 42 * } * * val nullablePair: Pair = null to 42 - * val result = nullablePair.toNotNull() + * val result = nullablePair.onlyWithValues() * println(result) // Output: null * ``` */ -fun Pair.toNotNull(): Pair? = +fun Pair.onlyWithValues(): Pair? = runCatching { requireNotNull(first) to requireNotNull(second) }.getOrNull() diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt new file mode 100644 index 00000000..0d5036b7 --- /dev/null +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseChain.kt @@ -0,0 +1,287 @@ +@file:Suppress("Filename", "TooManyFunctions", "LongParameterList", "unused") + +package br.com.arch.toolkit.util + +import androidx.lifecycle.LiveData +import br.com.arch.toolkit.annotation.Experimental +import br.com.arch.toolkit.livedata.ResponseLiveData +import br.com.arch.toolkit.livedata.responseLiveData +import br.com.arch.toolkit.result.DataResult +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/* region LiveData + Response Functions --------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface WithResponse { + suspend fun invoke(result: T?): ResponseLiveData +} + +@Experimental +fun LiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, + condition: suspend (T?) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other::invoke, condition) + .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .collect(::emit) +} + +@Experimental +fun LiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, + condition: suspend (T?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other::invoke, condition) + .mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun LiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: WithResponse, + condition: suspend (T?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface NotNullWithResponse { + suspend fun invoke(result: T): ResponseLiveData +} + +@Experimental +fun LiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, + condition: suspend (T) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun LiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, + condition: suspend (T) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun LiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: NotNullWithResponse, + condition: suspend (T) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + LiveData Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface ResponseWith { + suspend fun invoke(result: DataResult?): LiveData +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other::invoke, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other::invoke, condition) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseWith, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@FunctionalInterface +fun interface ResponseNotNullWith { + suspend fun invoke(result: DataResult): LiveData +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other::invoke, condition) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseNotNullWith, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + Response Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainWith(other, condition) + .mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult?) -> ResponseLiveData, + condition: suspend (DataResult?) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, +): ResponseLiveData> = responseLiveData(context = context) { + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalChainNotNullWith(other, condition) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.chainNotNullWith( + context: CoroutineContext = EmptyCoroutineContext, + other: suspend (DataResult) -> ResponseLiveData, + condition: suspend (DataResult) -> Boolean, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = chainNotNullWith( + context = context, + other = other, + condition = condition, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ diff --git a/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt new file mode 100644 index 00000000..de85f460 --- /dev/null +++ b/toolkit/event-observer/src/main/kotlin/br/com/arch/toolkit/util/_responseCombine.kt @@ -0,0 +1,252 @@ +@file:Suppress("Filename", "TooManyFunctions", "unused") + +package br.com.arch.toolkit.util + +import androidx.lifecycle.LiveData +import br.com.arch.toolkit.annotation.Experimental +import br.com.arch.toolkit.livedata.ResponseLiveData +import br.com.arch.toolkit.livedata.responseLiveData +import br.com.arch.toolkit.result.DataResult +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapNotNull +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/* region Operator Functions -------------------------------------------------------------------- */ +@Experimental +operator fun LiveData.plus(other: ResponseLiveData): ResponseLiveData> = + combine(context = EmptyCoroutineContext, other = other) + +@Experimental +operator fun ResponseLiveData.plus(source: LiveData): ResponseLiveData> = + combine(context = EmptyCoroutineContext, other = source) + +@Experimental +operator fun ResponseLiveData.plus(source: ResponseLiveData): ResponseLiveData> = + combine(context = EmptyCoroutineContext, other = source) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region LiveData + Response Functions --------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@Experimental +fun LiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .collect(::emit) +} + +@Experimental +fun LiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other).mapNotNull { (data, result) -> data?.let(::dataResultSuccess) + result } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun LiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental +fun LiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun LiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (data, result) -> (dataResultSuccess(data) + result).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun LiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + LiveData Functions ---------------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other) + .mapNotNull { (result, data) -> result + data?.let(::dataResultSuccess) } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (result, data) -> (result + dataResultSuccess(data)).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: LiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ + +/* region Response + Response Functions --------------------------------------------------------- */ +/* Nullable ------------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombine(other).mapNotNull { (resultA, resultB) -> resultA + resultB } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combine( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combine( + context = context, + other = other, + transform = Dispatchers.IO to transform +) + +/* Non Nullable --------------------------------------------------------------------------------- */ +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData +): ResponseLiveData> = responseLiveData(context = context) { + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: Pair>) -> DataResult> +): ResponseLiveData = responseLiveData(context = context) { + val (dispatcher, block) = transform + internalCombineNotNull(other) + .mapNotNull { (resultA, resultB) -> (resultA + resultB).onlyWithValues() } + .flowOn(dispatcher) + .mapNotNull { result -> runCatching { block(result) }.getOrElse(::dataResultError) } + .flowOn(context) + .collect(::emit) +} + +@Experimental +fun ResponseLiveData.combineNotNull( + context: CoroutineContext = EmptyCoroutineContext, + other: ResponseLiveData, + transform: suspend (DataResult>) -> DataResult +): ResponseLiveData = combineNotNull( + context = context, + other = other, + transform = Dispatchers.IO to transform +) +/* endregion ------------------------------------------------------------------------------------ */ diff --git a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt index 5bf4e43d..44a5d358 100644 --- a/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt +++ b/toolkit/event-observer/src/test/kotlin/br/com/arch/toolkit/livedata/response/ResponseLiveDataTest.kt @@ -285,7 +285,7 @@ class ResponseLiveDataTest { fun `09 - mergeWith - plus`() = runTest { val liveDataA = ResponseLiveData(dataResultSuccess(123)) val liveDataB = ResponseLiveData(dataResultSuccess("String")) - val liveDataMerge = liveDataA + liveDataB + val liveDataMerge = liveDataA.mergeWith(liveDataB) Assert.assertEquals(dataResultSuccess(123), liveDataA.value) Assert.assertEquals(dataResultSuccess("String"), liveDataB.value) diff --git a/toolkit/recycler-adapter/build.gradle.kts b/toolkit/recycler-adapter/build.gradle.kts index 1dbb6fab..480de8e2 100644 --- a/toolkit/recycler-adapter/build.gradle.kts +++ b/toolkit/recycler-adapter/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("toolkit-publish") } -android.namespace = "br.com.arch.toolkit.playground.recyclerAdapter" +android.namespace = "br.com.arch.toolkit.recyclerAdapter" dependencies { // Libraries diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt deleted file mode 100644 index a762f292..00000000 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaderModel.kt +++ /dev/null @@ -1,5 +0,0 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader - -interface StickyHeaderModel { - var isSticky: Boolean -} diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt similarity index 96% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt index 76e5c22e..9a996963 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseRecyclerAdapter.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseRecyclerAdapter.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.content.Context import android.view.View @@ -6,7 +6,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaders +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaders /** * Basic implementation of RecyclerView.Adapter using AsyncListDiffer and CustomViews as items diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt similarity index 78% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt index 95b8b50f..ddaf35dd 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/BaseViewHolder.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/BaseViewHolder.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.view.View import androidx.recyclerview.widget.RecyclerView diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt similarity index 89% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt index 15a832f9..216097ac 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/DefaultItemDiffer.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/DefaultItemDiffer.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.annotation.SuppressLint import androidx.recyclerview.widget.DiffUtil diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt similarity index 91% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt index 86a74670..e54210b1 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/SimpleAdapter.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/SimpleAdapter.kt @@ -1,8 +1,8 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter import android.content.Context import android.view.View -import br.com.arch.toolkit.playground.recyclerAdapter.stickyheader.StickyHeaderModel +import br.com.arch.toolkit.recyclerAdapter.stickyheader.StickyHeaderModel /** * Implementation of BaseRecyclerAdapter representing a single viewType diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt similarity index 77% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt index 295aa192..7d026e6e 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/ViewBinder.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/ViewBinder.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter +package br.com.arch.toolkit.recyclerAdapter /** * Implement this on your CustomView classes to start using your BaseRecyclerAdapter implementations like a charm =) diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt new file mode 100644 index 00000000..8575255c --- /dev/null +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaderModel.kt @@ -0,0 +1,5 @@ +package br.com.arch.toolkit.recyclerAdapter.stickyheader + +interface StickyHeaderModel { + var isSticky: Boolean +} diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt similarity index 92% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt index 57c23df1..f11e1400 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeaders.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeaders.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader +package br.com.arch.toolkit.recyclerAdapter.stickyheader import android.view.View diff --git a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt similarity index 98% rename from toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt rename to toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt index 8024edbc..556d4f94 100644 --- a/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recycler/adapter/stickyheader/StickyHeadersLinearLayoutManager.kt +++ b/toolkit/recycler-adapter/src/main/kotlin/br/com/arch/toolkit/recyclerAdapter/stickyheader/StickyHeadersLinearLayoutManager.kt @@ -1,4 +1,4 @@ -package br.com.arch.toolkit.playground.recyclerAdapter.stickyheader +package br.com.arch.toolkit.recyclerAdapter.stickyheader import android.content.Context import android.view.View @@ -250,10 +250,10 @@ class StickyHeadersLinearLayoutManager : // - There's one to show; // - It's on the edge or it's not the anchor view; // - Isn't followed by another sticky header; - if (headerPos != -1 && - (headerPos != anchorPos || isViewOnBoundary(anchorView)) && - nextHeaderPos != headerPos + 1 - ) { + val oneToShow = headerPos != -1 + val onEdgeAndNotAnchor = (headerPos != anchorPos || isViewOnBoundary(anchorView)) + val notFollowedByNext = nextHeaderPos != headerPos + 1 + if (oneToShow && onEdgeAndNotAnchor && notFollowedByNext) { // Ensure existing sticky header, if any, is of correct type. if (mStickyHeader != null && mAdapter != null && diff --git a/tools/libs.versions.toml b/tools/libs.versions.toml index 4cfd9d06..1de8e218 100644 --- a/tools/libs.versions.toml +++ b/tools/libs.versions.toml @@ -1,12 +1,8 @@ # ------------------------------------------------------------------------------------------------ # [versions] ## --- Plugins and more general stuff --- ## -android_plugin = "8.1.4" jacoco = "0.8.9" -dexcount = "4.0.0" -pedrinho_publish = "0.0.2" detekt = "1.23.3" -ktlint = "11.6.1" ## --- Build Config and SDK --- ## build-version-name = "1.0.0" @@ -18,7 +14,7 @@ build-sdk-target = "35" build-tools = "35.0.0" ## JetBrains -jetbrains-kotlin = "2.0.0" +jetbrains-kotlin = "2.0.10" jetbrains-coroutines = "1.8.1" jetbrains-serialization = "1.6.3" jetbrains-kover = "0.7.4" @@ -55,7 +51,6 @@ square-timber = "5.0.1" ## Google google-material = "1.12.0" google-gson = "2.11.0" -google-ksp = "2.0.0-1.0.22" ## Tools mockito = "5.6.0" @@ -125,8 +120,6 @@ junit-test = { group = "junit", name = "junit", version.ref = "junit" } robolectric-test = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" } - - # ------------------------------------------------------------------------------------------------ # ## Comment this when you start to update some libraries x-normalize-001 = { group = "androidx.activity", name = "activity", version.ref = "androidx-compose-activity" } @@ -140,8 +133,8 @@ x-normalize-008 = { group = "androidx.customview", name = "customview", version x-normalize-009 = { group = "androidx.drawerlayout", name = "drawerlayout", version = "1.2.0" } x-normalize-010 = { group = "androidx.lifecycle", name = "lifecycle-common", version.ref = "androidx-lifecycle" } x-normalize-011 = { group = "androidx.viewpager2", name = "viewpager2", version = "1.1.0" } -x-normalize-012 = { group = "androidx.test", name = "monitor", version = "1.7.1" } -x-normalize-013 = { group = "androidx.test", name = "runner", version = "1.6.1" } +x-normalize-012 = { group = "androidx.test", name = "monitor", version = "1.7.2" } +x-normalize-013 = { group = "androidx.test", name = "runner", version = "1.6.2" } x-normalize-014 = { group = "com.google.auto.value", name = "auto-value-annotations", version = "1.9" } x-normalize-015 = { group = "com.google.code.findbugs", name = "jsr305", version = "3.0.2" } x-normalize-016 = { group = "com.squareup", name = "javawritter", version = "2.5.0" } @@ -153,25 +146,6 @@ x-normalize-020 = { group = "org.jetbrains", name = "annotations", version = "23 # ------------------------------------------------------------------------------------------------ # [plugins] -## Android -android-application = { id = "com.android.application", version.ref = "android_plugin" } -android-library = { id = "com.android.library", version.ref = "android_plugin" } - -## Android -google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" } - -## JetBrains -jetbrains-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "jetbrains-kotlin" } -jetbrains-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "jetbrains-kover" } -jetbrains-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "jetbrains-kotlin" } -jetbrains-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "jetbrains-kotlin" } - -## Tools to make my life easier -dexcount = { id = "com.getkeepsafe.dexcount", version.ref = "dexcount" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint-idea", version.ref = "ktlint" } -pedrinho_publish = { id = "io.github.pedro-bachiega.sonatype-maven-publish", version.ref = "pedrinho_publish" } - # ------------------------------------------------------------------------------------------------ # [bundles] diff --git a/tools/plugin.versions.toml b/tools/plugin.versions.toml index a8f92f77..63ac6585 100644 --- a/tools/plugin.versions.toml +++ b/tools/plugin.versions.toml @@ -1,10 +1,13 @@ # ------------------------------------------------------------------------------------------------ # [versions] -android_plugin = "8.1.4" +android_plugin = "8.5.2" detekt = "1.23.3" ktlint = "11.6.1" -jetbrains-kotlin = "2.0.0" +jetbrains-kotlin = "2.0.10" jetbrains-kover = "0.7.4" +google-ksp = "2.0.10-1.0.24" +pedrinho_publish = "0.0.2" +dexcount = "4.0.0" # ------------------------------------------------------------------------------------------------ # [libraries] @@ -23,4 +26,23 @@ ktlint = { group = "org.jlleitschuh.gradle.ktlint-idea", name = "org.jlleitschuh # ------------------------------------------------------------------------------------------------ # [plugins] -jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin" } \ No newline at end of file +jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrains-kotlin" } + +## Android +android-application = { id = "com.android.application", version.ref = "android_plugin" } +android-library = { id = "com.android.library", version.ref = "android_plugin" } + +## Android +google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" } + +## JetBrains +jetbrains-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "jetbrains-kotlin" } +jetbrains-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "jetbrains-kover" } +jetbrains-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "jetbrains-kotlin" } +jetbrains-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "jetbrains-kotlin" } + +## Tools to make my life easier +dexcount = { id = "com.getkeepsafe.dexcount", version.ref = "dexcount" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint-idea", version.ref = "ktlint" } +pedrinho_publish = { id = "io.github.pedro-bachiega.sonatype-maven-publish", version.ref = "pedrinho_publish" }