diff --git a/dependencies.gradle b/dependencies.gradle index bac45fa..280b1d3 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -4,10 +4,10 @@ ext { buildToolsVersion: "29.0.3", minSdkVersion : 14, library : [ - publishGroupId: 'io.github.vsukharew', + publishGroupId : 'io.github.vsukharew', publishArtifactId: 'anytypeadapter', - versionName: "1.0.11", - versionCode: 1, + versionName : "1.2.0", + versionCode : 1, ], sample : [ versionName: "1.0", @@ -41,7 +41,8 @@ ext { moxy : '2.1.2', retrofit : '2.8.1', leakcanary : '2.2', - junit : '4.12' + junit : '5.7.1', + mockito : '3.2.0', ] androidx = [ appCompat : "androidx.appcompat:appcompat:${versions.androidx.appCompat}", @@ -72,5 +73,6 @@ ext { compiler: "com.google.dagger:dagger-compiler:${versions.dagger}", dagger : "com.google.dagger:dagger:${versions.dagger}", ] - junit = "junit:junit:${versions.junit}" + junit = "org.junit.jupiter:junit-jupiter-engine:${versions.junit}" + mockito = "org.mockito.kotlin:mockito-kotlin:${versions.mockito}" } \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle index e560770..b22a06c 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply from: '../dependencies.gradle' -apply from: "../scripts/publish-maven-central.gradle" +apply from: "../scripts/publishing.gradle" android { compileSdkVersion build.compileSdkVersion @@ -26,6 +26,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + testOptions { + unitTests.all { + useJUnitPlatform() + } + } } dependencies { @@ -37,6 +42,7 @@ dependencies { implementation androidx.constraintLayout implementation androidx.recyclerView testImplementation junit + testImplementation mockito androidTestImplementation androidx.testRunner androidTestImplementation androidx.espresso } \ No newline at end of file diff --git a/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeAdapter.kt b/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeAdapter.kt index 2d8c5f2..1f4ef6f 100644 --- a/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeAdapter.kt +++ b/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeAdapter.kt @@ -41,10 +41,9 @@ open class AnyTypeAdapter : RecyclerView.Adapter, - adapterPosition: Int - ): Int { - return with(positionsRanges) { - binarySearch { - when { - adapterPosition in it -> 0 - adapterPosition < it.first -> 1 - else -> -1 - } - } - } - } - private class DiffUtilCallback( private val oldList: List>, private val newList: List> diff --git a/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeCollection.kt b/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeCollection.kt index 1ac4b56..d414b88 100644 --- a/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeCollection.kt +++ b/library/src/main/java/vsukharev/anytypeadapter/adapter/AnyTypeCollection.kt @@ -7,6 +7,7 @@ import vsukharev.anytypeadapter.item.AdapterItemMetaData import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding import vsukharev.anytypeadapter.delegate.NoDataDelegate +import java.lang.IllegalStateException /** * Class that wraps items for [AnyTypeAdapter] @@ -23,16 +24,43 @@ class AnyTypeCollection private constructor( /** * Saved position value provided in [RecyclerView.Adapter.getItemViewType] */ - var currentItemViewTypePosition: Int = 0 + internal var currentItemViewTypePosition: Int = NO_POSITION /** * Returns delegate at the given position in the [itemsMetaData] collection */ val currentItemViewTypeDelegate: AnyTypeDelegate> - get() = itemsMetaData[currentItemViewTypePosition].delegate + get() { + return if (itemsMetaData.isNotEmpty()) { + itemsMetaData[currentItemViewTypePosition].delegate + } else { + throw IllegalStateException("Unable to get a delegate in an empty collection") + } + } val size: Int = items.size + /** + * Finds position for the current item view type given current [adapterPosition] + * @see [AnyTypeCollection.itemsMetaData] + */ + fun findCurrentItemViewTypePosition(adapterPosition: Int): Int { + val currentPositionsRange = positionsRanges.getOrNull(currentItemViewTypePosition) + return if (currentPositionsRange?.contains(adapterPosition) == true) { + currentItemViewTypePosition + } else { + with(positionsRanges) { + binarySearch { + when { + adapterPosition in it -> 0 + adapterPosition < it.first -> 1 + else -> -1 + } + } + } + } + } + class Builder { private val items = mutableListOf>() private val itemsMetaData = mutableListOf>() @@ -110,9 +138,9 @@ class AnyTypeCollection private constructor( * Adds [items] and the corresponding [delegate] only if [predicate] is true * @param predicate the condition determining whether the items and delegate should be added */ - fun , V>> addIf( + fun > addIf( items: List, - delegate: AnyTypeDelegate, V, H>, + delegate: AnyTypeDelegate, predicate: () -> Boolean ): Builder { return apply { @@ -137,30 +165,47 @@ class AnyTypeCollection private constructor( } } + /** + * Adds [item] and the corresponding [delegate] only if the item (which must be a collection) is not empty + */ + fun , V : ViewBinding, H : AnyTypeViewHolder> addIfNotEmpty( + item: T, + delegate: AnyTypeDelegate + ): Builder { + return apply { addIf(item, delegate) { item.count() > 0 } } + } + /** * Adds [items] and the corresponding [delegate] only if the list of items is not empty */ - fun , V>> addIfNotEmpty( + fun > addIfNotEmpty( items: List, - delegate: AnyTypeDelegate, V, H> + delegate: AnyTypeDelegate ): Builder { return apply { addIf(items, delegate) { items.isNotEmpty() } } } fun build(): AnyTypeCollection { val positionsRanges = with(itemsMetaData) { - zipWithNext { first, second -> - first.position until second.position - } + when { + when { isEmpty() -> emptyList() - else -> listOf(last().position until items.size) + else -> { + zipWithNext { first, second -> + first.position until second.position + } + listOf(last().position until items.size) + } + } + } + return AnyTypeCollection(items, itemsMetaData, positionsRanges).apply { + if (items.isNotEmpty()) { + currentItemViewTypePosition = 0 } } - return AnyTypeCollection(items, itemsMetaData, positionsRanges) } } companion object { + internal const val NO_POSITION = -1 val EMPTY = Builder().build() } } \ No newline at end of file diff --git a/library/src/main/java/vsukharev/anytypeadapter/delegate/NoDataDelegate.kt b/library/src/main/java/vsukharev/anytypeadapter/delegate/NoDataDelegate.kt index 2274516..97cb5f7 100644 --- a/library/src/main/java/vsukharev/anytypeadapter/delegate/NoDataDelegate.kt +++ b/library/src/main/java/vsukharev/anytypeadapter/delegate/NoDataDelegate.kt @@ -1,10 +1,8 @@ package vsukharev.anytypeadapter.delegate -import android.view.LayoutInflater -import android.view.View import androidx.viewbinding.ViewBinding -import vsukharev.anytypeadapter.holder.NoDataViewHolder import vsukharev.anytypeadapter.adapter.AnyTypeAdapter +import vsukharev.anytypeadapter.holder.NoDataViewHolder import java.util.* /** @@ -12,13 +10,8 @@ import java.util.* * This delegate can be used when creating list for [AnyTypeAdapter] without specifying data to bind */ abstract class NoDataDelegate : AnyTypeDelegate>() { - - override fun getItemId(item: Unit): String = ITEM_ID - - private companion object { - /** - * This delegate has an "artificial" identifier because there is no data - */ - val ITEM_ID: String = UUID.randomUUID().toString() - } + /** + * This delegate has an artificial identifier because there is no data + */ + override fun getItemId(item: Unit): String = UUID.randomUUID().toString() } \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/ExampleUnitTest.kt b/library/src/test/java/vsukharev/anytypeadapter/ExampleUnitTest.kt deleted file mode 100644 index 31c14ff..0000000 --- a/library/src/test/java/vsukharev/anytypeadapter/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package vsukharev.anytypeadapter - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionBuilderTest.kt b/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionBuilderTest.kt new file mode 100644 index 0000000..38e8627 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionBuilderTest.kt @@ -0,0 +1,199 @@ +package vsukharev.anytypeadapter.adapter + +import org.junit.jupiter.api.Test +import org.mockito.kotlin.* +import vsukharev.anytypeadapter.common.MockInitializer +import vsukharev.anytypeadapter.domain.Activity +import vsukharev.anytypeadapter.domain.Track + +class AnyTypeCollectionBuilderTest : MockInitializer() { + + @Test + fun add_addItemsOfDifferentViewTypes_eachTimeViewTypeIsDifferentNewItemIsAddedToItemsMetaDataCollection() { + var itemsCount = 0 + var itemsMetadataCount = 0 + AnyTypeCollection.Builder() + .apply { + repeat((1..10).count()) { + add(Track(), trackDelegate).also { itemsCount++ } + }.also { itemsMetadataCount++ } + add(headerDelegate).also { itemsCount++; itemsMetadataCount++ } + repeat((1..10).count()) { + add(Activity(), activityDelegate).also { itemsCount++ } + }.also { itemsMetadataCount++ } + } + .build() + .apply { + assert(itemsMetaData.size == itemsMetadataCount && items.size == itemsCount) + } + } + + @Test + fun add_addItemsOfDifferentViewTypes_verifyGetItemIdCalledEachTimeWhenItemAdded() { + val tracksCount = 10 + val activitiesCount = 10 + AnyTypeCollection.Builder() + .apply { + repeat((1..tracksCount).count()) { + add(Track(), trackDelegate) + } + add(headerDelegate) + repeat((1..activitiesCount).count()) { + add(Activity(), activityDelegate) + } + + verify(trackDelegate, times(tracksCount)).getItemId(any()) + verify(headerDelegate, times(1)).getItemId(any()) + verify(activityDelegate, times(activitiesCount)).getItemId(any()) + } + } + + @Test + fun add_addItemsOfDifferentViewTypes_verifyGetItemViewTypeCalledTwiceEachTimeItemAddedExceptCornerAdditions() { + val tracksList = listOf(Track(), Track()) + val activitiesCount = 10 + AnyTypeCollection.Builder() + .apply { + add(tracksList, trackDelegate) //corner addition + add(headerDelegate) + add(tracksList, trackListDelegate) + repeat((1..activitiesCount).count()) { + add(Activity(), activityDelegate) + } //corner addition + + verify(trackDelegate, times((tracksList.size * 2 - 1))).getItemViewType() + verify(headerDelegate, times(2)).getItemViewType() + verify(trackListDelegate, times(tracksList.size)).getItemViewType() + verify(activityDelegate, times((activitiesCount * 2 - 1))).getItemViewType() + } + } + + @Test + fun add_addSingleItem_verifyGetItemIdCalledWithAddedItem() { + val track = Track() + val captor = argumentCaptor() + AnyTypeCollection.Builder() + .apply { + add(track, trackDelegate) + verify(trackDelegate).getItemId(captor.capture()) + assert(track == captor.lastValue) + } + } + + @Test + fun add_addList_verifyGetItemIdCalledWithEachListItem() { + val tracksList = listOf(Track(), Track()) + val captor = argumentCaptor() + AnyTypeCollection.Builder() + .apply { + add(tracksList, trackDelegate) + verify(trackDelegate, times(tracksList.size)).getItemId(captor.capture()) + assert(tracksList == captor.allValues) + } + } + + @Test + fun addIf_predicateIsEitherTrueEitherFalse_itemIsAddedOnlyWhenPredicateIsTrue() { + var itemsCount = 0 + var itemsMetadataCount = 0 + AnyTypeCollection.Builder() + .apply { + repeat((1..10).count()) { + (addIf(Track(), trackDelegate) { it % 2 == 0 }).also { itemsCount++ } + }.also { itemsMetadataCount++ } + } + .build() + .apply { + assert(itemsMetaData.size == itemsMetadataCount && size == itemsCount / 2) + } + } + + @Test + fun addIf_predicateIsEitherTrueEitherFalse_noDataItemIsAddedOnlyWhenPredicateIsTrue() { + var itemsCount = 0 + var itemsMetadataCount = 0 + AnyTypeCollection.Builder() + .addIf(headerDelegate) { true }.also { itemsCount++; itemsMetadataCount++ } + .addIf(headerDelegate) { false }.also { itemsCount++ } + .build() + .apply { assert(itemsMetaData.size == itemsMetadataCount && size != itemsCount && size == 1) } + } + + @Test + fun addIf_dataIsListAndPredicateIsEitherTrueEitherFalse_itemIsAddedOnlyWhenPredicateIsTrue() { + var itemsCount = 0 + var itemsMetadataCount = 0 + AnyTypeCollection.Builder() + .apply { + repeat((1..10).count()) { + (addIf(listOf(Track()), trackDelegate) { it % 2 == 0 }).also { itemsCount++ } + }.also { itemsMetadataCount++ } + } + .build() + .apply { + assert(itemsMetaData.size == itemsMetadataCount && items.size == itemsCount / 2) + } + } + + @Test + fun addIf_predicateIsEitherTrueEitherFalse_verifyGetItemIdCalledOnlyWhenPredicateIsTrue() { + var itemsCount = 0 + var itemsMetadataCount = 0 + AnyTypeCollection.Builder() + .apply { + repeat((1..10).count()) { + (addIf(Track(), trackDelegate) { it % 2 == 0 }).also { itemsCount++ } + }.also { itemsMetadataCount++ } + + verify(trackDelegate, times((itemsCount / 2))).getItemId(any()) + } + } + + @Test + fun addIfNotEmpty_inputListIsEmpty_dataListShouldNotBeAdded() { + AnyTypeCollection.Builder() + .addIfNotEmpty(emptyList(), trackDelegate) + .addIfNotEmpty(emptyList(), trackListDelegate) + .build() + .apply { assert(itemsMetaData.isEmpty() && items.isEmpty()) } + } + + @Test + fun addIfNotEmpty_inputListIsEmpty_noneDelegateMethodsGetCalled() { + AnyTypeCollection.Builder() + .addIfNotEmpty(emptyList(), trackDelegate) + .addIfNotEmpty(emptyList(), trackListDelegate) + .apply { + verifyZeroInteractions(trackDelegate) + verifyZeroInteractions(trackListDelegate) + } + } + + @Test + fun addIfNotEmpty_inputListIsNotEmpty_dataListShouldBeAdded() { + AnyTypeCollection.Builder() + .addIfNotEmpty(listOf(Track()), trackDelegate) + .addIfNotEmpty(listOf(Track()), trackListDelegate) + .build() + .apply { assert(itemsMetaData.isNotEmpty() && items.isNotEmpty()) } + } + + @Test + fun addIfNotEmpty_inputListIsNotEmpty_delegateMethodsGetCalled() { + AnyTypeCollection.Builder() + .addIfNotEmpty(listOf(Track()), trackDelegate) + .addIfNotEmpty(listOf(Track()), trackListDelegate) + .apply { + verify(trackDelegate).getItemId(any()) + verify(trackListDelegate).getItemId(any()) + } + } + + @Test + fun build_emptyCollection_shouldGetEmptyCollectionOfRanges() { + val collection = AnyTypeCollection.EMPTY + val expected = emptyList() + val actual = collection.positionsRanges + assert(expected == actual) + } +} \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionTest.kt b/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionTest.kt new file mode 100644 index 0000000..a2bfcf1 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/adapter/AnyTypeCollectionTest.kt @@ -0,0 +1,55 @@ +package vsukharev.anytypeadapter.adapter + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows +import vsukharev.anytypeadapter.common.MockInitializer +import vsukharev.anytypeadapter.domain.Activity +import vsukharev.anytypeadapter.domain.Track + +class AnyTypeCollectionTest : MockInitializer() { + + @Test + fun currentItemViewTypeDelegate_collectionIsEmpty_exceptionShouldBeThrown() { + val collection = AnyTypeCollection.EMPTY + assertThrows { collection.currentItemViewTypeDelegate } + } + + @Test + fun currentItemViewTypeDelegate_collectionIsNotEmpty_exceptionShouldNotBeThrown() { + AnyTypeCollection.Builder() + .add(headerDelegate) + .build() + .apply { + assertDoesNotThrow { currentItemViewTypeDelegate } + assert(currentItemViewTypeDelegate::class == headerDelegate::class) + } + } + + @Test + fun currentItemViewTypePosition_collectionIsEmpty_shouldBeEqualToNoPosition() { + val collection = AnyTypeCollection.EMPTY + assert(collection.currentItemViewTypePosition == AnyTypeCollection.NO_POSITION) + } + + @Test + fun findCurrentItemViewType_iterateThroughCollection_whileItemsAreOfSameFindCurrentItemViewTypePositionReturnsSavedValue() { + AnyTypeCollection.Builder() + .add(listOf(Track(), Track(), Track()), trackDelegate) + .add(headerDelegate) + .add(listOf(Activity(), Activity()), activityDelegate) + .build() + .apply { + for (i in 1 until size) { + val position = findCurrentItemViewTypePosition(i) + if (items[i].data::class == items[i - 1].data::class) { + assert(position == currentItemViewTypePosition) + } else { + assert(position != currentItemViewTypePosition) + currentItemViewTypePosition = position // in real code, this saving takes place in adapter + } + } + assert(currentItemViewTypePosition == itemsMetaData.size - 1) + } + } +} \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/common/MockInitializer.kt b/library/src/test/java/vsukharev/anytypeadapter/common/MockInitializer.kt new file mode 100644 index 0000000..600f9e9 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/common/MockInitializer.kt @@ -0,0 +1,39 @@ +package vsukharev.anytypeadapter.common + +import androidx.viewbinding.ViewBinding +import org.junit.jupiter.api.BeforeEach +import org.mockito.Mockito.`when` +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import vsukharev.anytypeadapter.delegate.AnyTypeDelegate +import vsukharev.anytypeadapter.delegate.NoDataDelegate +import vsukharev.anytypeadapter.domain.Activity +import vsukharev.anytypeadapter.domain.Track +import vsukharev.anytypeadapter.holder.AnyTypeViewHolder +import java.util.* + +open class MockInitializer { + protected val headerDelegate = mock>() + protected val trackDelegate = + mock>>() + protected val activityDelegate = + mock>>() + protected val trackListDelegate = + mock, ViewBinding, AnyTypeViewHolder, ViewBinding>>>() + protected val trackHolder = mock>() + + @BeforeEach + protected fun initMocks() { + `when`(headerDelegate.getItemViewType()).thenReturn(0) + `when`(headerDelegate.getItemId(any())).thenReturn(UUID.randomUUID().toString()) + + `when`(trackDelegate.getItemViewType()).thenReturn(1) + `when`(trackDelegate.getItemId(any())).thenReturn(UUID.randomUUID().toString()) + + `when`(activityDelegate.getItemViewType()).thenReturn(2) + `when`(activityDelegate.getItemId(any())).thenReturn(UUID.randomUUID().toString()) + + `when`(trackListDelegate.getItemViewType()).thenReturn(3) + `when`(trackListDelegate.getItemId(any())).thenReturn(UUID.randomUUID().toString()) + } +} \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/delegate/AnyTypeDelegateTest.kt b/library/src/test/java/vsukharev/anytypeadapter/delegate/AnyTypeDelegateTest.kt new file mode 100644 index 0000000..365d9f4 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/delegate/AnyTypeDelegateTest.kt @@ -0,0 +1,20 @@ +package vsukharev.anytypeadapter.delegate + +import org.junit.jupiter.api.Test +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import vsukharev.anytypeadapter.common.MockInitializer +import vsukharev.anytypeadapter.domain.Track +import vsukharev.anytypeadapter.item.AdapterItem + +class AnyTypeDelegateTest : MockInitializer() { + + @Test + fun bind_callWithTrack_verifyHolderBindGetCalledWithTheSameTrack() { + val track = Track() + val captor = argumentCaptor() + trackDelegate.bind(AdapterItem(track.id, track), trackHolder) + verify(trackHolder).bind(captor.capture()) + assert(track == captor.firstValue) + } +} \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/domain/Activity.kt b/library/src/test/java/vsukharev/anytypeadapter/domain/Activity.kt new file mode 100644 index 0000000..f9ab2f3 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/domain/Activity.kt @@ -0,0 +1,8 @@ +package vsukharev.anytypeadapter.domain + +import java.util.* + +data class Activity( + val name: String = UUID.randomUUID().toString(), + val id: String = UUID.randomUUID().toString() +) \ No newline at end of file diff --git a/library/src/test/java/vsukharev/anytypeadapter/domain/Track.kt b/library/src/test/java/vsukharev/anytypeadapter/domain/Track.kt new file mode 100644 index 0000000..c535f40 --- /dev/null +++ b/library/src/test/java/vsukharev/anytypeadapter/domain/Track.kt @@ -0,0 +1,8 @@ +package vsukharev.anytypeadapter.domain + +import java.util.* + +data class Track( + val name: String = UUID.randomUUID().toString(), + val id: String = UUID.randomUUID().toString() +) \ No newline at end of file diff --git a/scripts/publish-maven-central.gradle b/scripts/publishing.gradle similarity index 78% rename from scripts/publish-maven-central.gradle rename to scripts/publishing.gradle index a7d5ccb..ada10fe 100644 --- a/scripts/publish-maven-central.gradle +++ b/scripts/publishing.gradle @@ -1,6 +1,6 @@ -task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.source +tasks.register("androidSourcesJar", Jar) { + from android.sourceSets.main.java.srcDirs + archiveClassifier.set('sources') } artifacts { @@ -39,16 +39,15 @@ afterEvaluate { publishing { publications { release(MavenPublication) { + from components.release + artifact androidSourcesJar + // The coordinates of the library, being set from variables that // we'll set up in a moment groupId build.library.publishGroupId artifactId build.library.publishArtifactId version build.library.versionName - // Two artifacts, the `aar` and the sources - artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") - artifact androidSourcesJar - // Self-explanatory metadata for the most part pom { name = build.library.publishArtifactId @@ -74,18 +73,6 @@ afterEvaluate { developerConnection = 'scm:git:ssh://github.com/vsukharew/AnyTypeAdapter' url = 'https://github.com/vsukharew/AnyTypeAdapter/tree/master' } - // A slightly hacky fix so that your POM will include any transitive dependencies - // that your library builds upon - withXml { - def dependenciesNode = asNode().appendNode('dependencies') - - project.configurations.implementation.allDependencies.each { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', it.group) - dependencyNode.appendNode('artifactId', it.name) - dependencyNode.appendNode('version', it.version) - } - } } } } @@ -111,8 +98,7 @@ afterEvaluate { } } } -} - -signing { - sign publishing.publications + signing { + sign publishing.publications + } } \ No newline at end of file