Skip to content

Commit

Permalink
Merge pull request #86 from vsukharew/release/v.1.2.0
Browse files Browse the repository at this point in the history
* edited publishing script

* changed AnyTypeCollection.Builder methods:
- addIf now along with items of List<T> type takes delegate typed with <T, V, H> instead of <List<T>, V, H>
- addIfNotEmpty now along with items of List<T> type takes delegate typed with <T, V, H> instead of <List<T>, V, H>
- another overload of addIfNotEmpty added

* Add unit tests for AnyTypeCollection, AnyTypeCollection.Builder, AnyTypeDelegate

* - moved findCurrentItemViewTypePosition() from AnyTypeAdapter.kt to AnyTypeCollection.kt
- added tests for AnyTypeCollection.kt

* fixed returning the same id for each NoDataDelegate
  • Loading branch information
vsukharew committed Nov 2, 2021
2 parents bcda5c8 + a53ba49 commit 9b786a8
Show file tree
Hide file tree
Showing 13 changed files with 416 additions and 93 deletions.
12 changes: 7 additions & 5 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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}",
Expand Down Expand Up @@ -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}"
}
8 changes: 7 additions & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -26,6 +26,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
testOptions {
unitTests.all {
useJUnitPlatform()
}
}
}

dependencies {
Expand All @@ -37,6 +42,7 @@ dependencies {
implementation androidx.constraintLayout
implementation androidx.recyclerView
testImplementation junit
testImplementation mockito
androidTestImplementation androidx.testRunner
androidTestImplementation androidx.espresso
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ open class AnyTypeAdapter : RecyclerView.Adapter<AnyTypeViewHolder<Any, ViewBind
override fun getItemCount(): Int = anyTypeCollection.size

override fun getItemViewType(position: Int): Int {
return with(anyTypeCollection) {
findCurrentItemViewTypePosition(positionsRanges, position)
.also { currentItemViewTypePosition = it }
.let { currentItemViewTypeDelegate.getItemViewType() }
return anyTypeCollection.run {
findCurrentItemViewTypePosition(position).also { currentItemViewTypePosition = it }
currentItemViewTypeDelegate.getItemViewType()
}
}

Expand Down Expand Up @@ -88,26 +87,6 @@ open class AnyTypeAdapter : RecyclerView.Adapter<AnyTypeViewHolder<Any, ViewBind
}
}

/**
* Finds position inside [anyTypeCollection] for the current item view type
* given current [adapterPosition]
* @see [AnyTypeCollection.itemsMetaData]
*/
private fun findCurrentItemViewTypePosition(
positionsRanges: List<IntRange>,
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<AdapterItem<Any>>,
private val newList: List<AdapterItem<Any>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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<Any, ViewBinding, AnyTypeViewHolder<Any, ViewBinding>>
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<AdapterItem<Any>>()
private val itemsMetaData = mutableListOf<AdapterItemMetaData<Any, ViewBinding>>()
Expand Down Expand Up @@ -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 <T : Any, V : ViewBinding, H : AnyTypeViewHolder<List<T>, V>> addIf(
fun <T : Any, V : ViewBinding, H : AnyTypeViewHolder<T, V>> addIf(
items: List<T>,
delegate: AnyTypeDelegate<List<T>, V, H>,
delegate: AnyTypeDelegate<T, V, H>,
predicate: () -> Boolean
): Builder {
return apply {
Expand All @@ -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 <T : Iterable<*>, V : ViewBinding, H : AnyTypeViewHolder<T, V>> addIfNotEmpty(
item: T,
delegate: AnyTypeDelegate<T, V, H>
): 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 <T : Any, V : ViewBinding, H : AnyTypeViewHolder<List<T>, V>> addIfNotEmpty(
fun <T : Any, V : ViewBinding, H : AnyTypeViewHolder<T, V>> addIfNotEmpty(
items: List<T>,
delegate: AnyTypeDelegate<List<T>, V, H>
delegate: AnyTypeDelegate<T, V, H>
): 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()
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
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.*

/**
* [AnyTypeDelegate] that creates [NoDataViewHolder]
* This delegate can be used when creating list for [AnyTypeAdapter] without specifying data to bind
*/
abstract class NoDataDelegate<V: ViewBinding> : AnyTypeDelegate<Unit, V, NoDataViewHolder<V>>() {

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()
}
17 changes: 0 additions & 17 deletions library/src/test/java/vsukharev/anytypeadapter/ExampleUnitTest.kt

This file was deleted.

Loading

0 comments on commit 9b786a8

Please sign in to comment.