diff --git a/repos/android/build.gradle b/android/recyclerview/build.gradle
similarity index 66%
rename from repos/android/build.gradle
rename to android/recyclerview/build.gradle
index aa5218eaf68..c130272d88d 100644
--- a/repos/android/build.gradle
+++ b/android/recyclerview/build.gradle
@@ -11,13 +11,12 @@ kotlin {
sourceSets {
commonMain {
dependencies {
- api internalProject("micro_utils.repos.common")
- api internalProject("micro_utils.coroutines")
+ api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
}
}
androidMain {
dependencies {
- implementation "androidx.core:core-ktx:$core_ktx_version"
+ api "androidx.recyclerview:recyclerview:$androidx_recycler_version"
}
}
}
diff --git a/android/recyclerview/src/main/AndroidManifest.xml b/android/recyclerview/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..d08d0502fee
--- /dev/null
+++ b/android/recyclerview/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractStandardViewHolder.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractStandardViewHolder.kt
new file mode 100644
index 00000000000..ebcbb481015
--- /dev/null
+++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractStandardViewHolder.kt
@@ -0,0 +1,22 @@
+package dev.inmo.micro_utils.android.recyclerview
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+
+abstract class AbstractStandardViewHolder(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ viewId: Int,
+ onViewInflated: ((View) -> Unit)? = null
+) : AbstractViewHolder(
+ inflater.inflate(viewId, container, false).also {
+ onViewInflated ?.invoke(it)
+ }
+) {
+ constructor(
+ container: ViewGroup,
+ viewId: Int,
+ onViewInflated: ((View) -> Unit)? = null
+ ) : this(LayoutInflater.from(container.context), container, viewId, onViewInflated)
+}
diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractViewHolder.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractViewHolder.kt
new file mode 100644
index 00000000000..529bbbd4dd3
--- /dev/null
+++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/AbstractViewHolder.kt
@@ -0,0 +1,10 @@
+package dev.inmo.micro_utils.android.recyclerview
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+abstract class AbstractViewHolder(
+ view: View
+) : RecyclerView.ViewHolder(view) {
+ abstract fun onBind(item: T)
+}
diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/Divider.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/Divider.kt
new file mode 100644
index 00000000000..21b03ca80b0
--- /dev/null
+++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/Divider.kt
@@ -0,0 +1,9 @@
+package dev.inmo.micro_utils.android.recyclerview
+
+import android.content.Context
+import android.widget.LinearLayout
+import androidx.recyclerview.widget.DividerItemDecoration
+
+val Context.recyclerViewItemsDecoration
+ get() = DividerItemDecoration(this, LinearLayout.VERTICAL)
+
diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/LeftItems.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/LeftItems.kt
new file mode 100644
index 00000000000..fc6d51b864e
--- /dev/null
+++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/LeftItems.kt
@@ -0,0 +1,47 @@
+package dev.inmo.micro_utils.android.recyclerview
+
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.*
+
+fun RecyclerView.lastVisibleItemFlow(
+ completingScope: CoroutineScope
+): Flow {
+ val lastVisibleElementFun: () -> Int = (layoutManager as? LinearLayoutManager) ?.let { it::findLastVisibleItemPosition } ?: error("Currently supported only linear layout manager")
+ val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun())
+ addOnScrollListener(
+ object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+ lastVisibleFlow.value = lastVisibleElementFun()
+ }
+ }.also { scrollListener ->
+ lastVisibleFlow.onCompletion {
+ removeOnScrollListener(scrollListener)
+ }.launchIn(completingScope)
+ }
+ )
+ return lastVisibleFlow.asStateFlow()
+}
+
+inline fun Flow.mapLeftItems(
+ crossinline countGetter: () -> Int
+): Flow = map { countGetter() - it }
+
+inline fun Flow.mapRequireFilling(
+ minimalLeftItems: Int,
+ crossinline countGetter: () -> Int
+): Flow = mapLeftItems(countGetter).mapNotNull {
+ if (it < minimalLeftItems) {
+ it
+ } else {
+ null
+ }
+}
+
+inline fun RecyclerView.mapRequireFilling(
+ minimalLeftItems: Int,
+ completingScope: CoroutineScope,
+ crossinline countGetter: () -> Int
+): Flow = lastVisibleItemFlow(completingScope).mapRequireFilling(minimalLeftItems, countGetter)
diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt
new file mode 100644
index 00000000000..96d1020fff6
--- /dev/null
+++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt
@@ -0,0 +1,68 @@
+package dev.inmo.micro_utils.android.recyclerview
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+
+abstract class RecyclerViewAdapter(
+ val data: List
+): RecyclerView.Adapter>() {
+ var emptyView: View? = null
+ set(value) {
+ field = value
+ checkEmpty()
+ }
+
+ init {
+ registerAdapterDataObserver(
+ object : RecyclerView.AdapterDataObserver() {
+ override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
+ super.onItemRangeChanged(positionStart, itemCount)
+ checkEmpty()
+ }
+
+ override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
+ super.onItemRangeChanged(positionStart, itemCount, payload)
+ checkEmpty()
+ }
+
+ override fun onChanged() {
+ super.onChanged()
+ checkEmpty()
+ }
+
+ override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
+ super.onItemRangeRemoved(positionStart, itemCount)
+ checkEmpty()
+ }
+
+ override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
+ super.onItemRangeMoved(fromPosition, toPosition, itemCount)
+ checkEmpty()
+ }
+
+ override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+ super.onItemRangeInserted(positionStart, itemCount)
+ checkEmpty()
+ }
+ }
+ )
+ checkEmpty()
+ }
+
+ override fun getItemCount(): Int = data.size
+
+ override fun onBindViewHolder(holder: AbstractViewHolder, position: Int) {
+ holder.onBind(data[position])
+ }
+
+ private fun checkEmpty() {
+ emptyView ?. let {
+ if (data.isEmpty()) {
+ it.visibility = View.VISIBLE
+ } else {
+ it.visibility = View.GONE
+ }
+ }
+ }
+}
diff --git a/build.gradle b/build.gradle
index ced5e4a2e35..c6d3e1911c6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,6 +14,7 @@ buildscript {
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
}
}
diff --git a/coroutines/build.gradle b/coroutines/build.gradle
index 6cfeab046d3..19bbfe5aaac 100644
--- a/coroutines/build.gradle
+++ b/coroutines/build.gradle
@@ -14,5 +14,10 @@ kotlin {
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
}
}
+ androidMain {
+ dependencies {
+ api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
+ }
+ }
}
}
\ No newline at end of file
diff --git a/coroutines/src/main/kotlin/dev/inmo/micro_utils/coroutines/DoInUI.kt b/coroutines/src/main/kotlin/dev/inmo/micro_utils/coroutines/DoInUI.kt
new file mode 100644
index 00000000000..f938e53655f
--- /dev/null
+++ b/coroutines/src/main/kotlin/dev/inmo/micro_utils/coroutines/DoInUI.kt
@@ -0,0 +1,10 @@
+package dev.inmo.micro_utils.coroutines
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+suspend inline fun doInUI(noinline block: suspend CoroutineScope.() -> T) = withContext(
+ Dispatchers.Main,
+ block
+)
diff --git a/dokka/build.gradle b/dokka/build.gradle
index b619aa90912..fee10660b29 100644
--- a/dokka/build.gradle
+++ b/dokka/build.gradle
@@ -1,26 +1,15 @@
-buildscript {
- repositories {
- mavenLocal()
- jcenter()
- mavenCentral()
- }
-
- dependencies {
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
- }
-}
-
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
- id "org.jetbrains.dokka" version "$dokka_version"
+ id "com.android.library"
+ id "kotlin-android-extensions"
+ id "org.jetbrains.dokka"
}
repositories {
mavenLocal()
jcenter()
+ google()
mavenCentral()
}
@@ -30,6 +19,7 @@ kotlin {
browser()
nodejs()
}
+ android {}
sourceSets {
commonMain {
@@ -41,6 +31,57 @@ kotlin {
it != project
&& it.hasProperty("kotlin")
&& it.kotlin.sourceSets.any { it.name.contains("commonMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("androidMain") }
+ ) {
+ api it
+ }
+ }
+ }
+ }
+ jsMain {
+ dependencies {
+ implementation kotlin('stdlib')
+
+ project.parent.subprojects.forEach {
+ if (
+ it != project
+ && it.hasProperty("kotlin")
+ && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("jsMain") }
+ ) {
+ api it
+ }
+ }
+ }
+ }
+ jvmMain {
+ dependencies {
+ implementation kotlin('stdlib')
+
+ project.parent.subprojects.forEach {
+ if (
+ it != project
+ && it.hasProperty("kotlin")
+ && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("jvmMain") }
+ ) {
+ api it
+ }
+ }
+ }
+ }
+ androidMain {
+ dependencies {
+ implementation kotlin('stdlib')
+
+ project.parent.subprojects.forEach {
+ if (
+ it != project
+ && it.hasProperty("kotlin")
+ && it.kotlin.sourceSets.any { it.name.contains("commonMain") }
+ && it.kotlin.sourceSets.any { it.name.contains("androidMain") }
) {
api it
}
@@ -84,5 +125,11 @@ tasks.dokkaHtml {
named("jvmMain") {
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
}
+
+ named("androidMain") {
+ sourceRoots.setFrom(findSourcesWithName("androidMain", "commonMain"))
+ }
}
}
+
+apply from: "$defaultAndroidSettingsPresetPath"
diff --git a/dokka/gradle.properties b/dokka/gradle.properties
index 55021b5f4e7..e9157af4da1 100644
--- a/dokka/gradle.properties
+++ b/dokka/gradle.properties
@@ -1,3 +1 @@
-dokka_version=1.4.0
-
org.gradle.jvmargs=-Xmx1024m
diff --git a/dokka/src/main/AndroidManifest.xml b/dokka/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..adef5c13dee
--- /dev/null
+++ b/dokka/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 250c56a1160..c0330ffc885 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -23,6 +23,7 @@ uuidVersion=0.2.2
# ANDROID
core_ktx_version=1.3.2
+androidx_recycler_version=1.1.0
android_minSdkVersion=24
android_compileSdkVersion=30
@@ -32,6 +33,12 @@ junit_version=4.12
test_ext_junit_version=1.1.2
espresso_core=3.3.0
+# Dokka
+
+dokka_version=1.4.0
+
+# Project data
+
group=dev.inmo
version=0.3.1
android_code_version=1
diff --git a/repos/android/src/main/AndroidManifest.xml b/repos/android/src/main/AndroidManifest.xml
deleted file mode 100644
index cbd8db25bb7..00000000000
--- a/repos/android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 4bfb57df815..811699ed9eb 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,6 +17,7 @@ String[] includes = [
":ktor:common",
":ktor:client",
":coroutines",
+ ":android:recyclerview",
":dokka"
]