mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-18 14:59:24 +00:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
2e9efc57de | |||
acecadef17 | |||
19394b5e69 | |||
de999e197f | |||
9d95687d3c | |||
aa9dfb4ab8 | |||
9c5b44efb3 | |||
ac587a67e6 | |||
59428140a8 | |||
60bdb59d71 | |||
be52871de8 | |||
b7934cf357 | |||
dbfbeef90a | |||
00943c9cdf | |||
8745c6a16a |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,5 +1,29 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.12.17
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `JB Compose`: `1.2.0-alpha01-dev774` -> `1.2.0-beta02`
|
||||||
|
* `Ktor`: `2.1.1` -> `2.1.2`
|
||||||
|
* `Koin`: `3.2.0` -> `3.2.2`
|
||||||
|
|
||||||
|
## 0.12.16
|
||||||
|
|
||||||
|
* `Coroutines`:
|
||||||
|
* `Android`:
|
||||||
|
* Add class `FlowOnHierarchyChangeListener`
|
||||||
|
* Add `ViewGroup#setOnHierarchyChangeListenerRecursively(OnHierarchyChangeListener)`
|
||||||
|
|
||||||
|
## 0.12.15
|
||||||
|
|
||||||
|
* `Common`:
|
||||||
|
* `applyDiff` will return `Diff` object since this release
|
||||||
|
* `Android`:
|
||||||
|
* New functions/extensions `findViewsByTag` and `findViewsByTagInActivity`
|
||||||
|
* `Coroutines`:
|
||||||
|
* Add `Flow` extensions `flatMap`, `flatMapNotNull` and `flatten`
|
||||||
|
* Add `Flow` extensions `takeNotNull` and `filterNotNull`
|
||||||
|
|
||||||
## 0.12.14
|
## 0.12.14
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@@ -14,6 +14,14 @@ private inline fun <T> getObject(
|
|||||||
/**
|
/**
|
||||||
* Diff object which contains information about differences between two [Iterable]s
|
* Diff object which contains information about differences between two [Iterable]s
|
||||||
*
|
*
|
||||||
|
* See tests for more info
|
||||||
|
*
|
||||||
|
* @param removed The objects which has been presented in the old collection but absent in new one. Index here is the index in the old collection
|
||||||
|
* @param added The object which appear in new collection only. Indexes here show the index in the new collection
|
||||||
|
* @param replaced Pair of old-new changes. First object has been presented in the old collection on its
|
||||||
|
* [IndexedValue.index] place, the second one is the object in new collection. Both have indexes due to the fact that in
|
||||||
|
* case when some value has been replaced after adds or removes in original collection the object index will be changed
|
||||||
|
*
|
||||||
* @see calculateDiff
|
* @see calculateDiff
|
||||||
*/
|
*/
|
||||||
data class Diff<T> internal constructor(
|
data class Diff<T> internal constructor(
|
||||||
@@ -165,7 +173,7 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
|
|||||||
fun <T> MutableList<T>.applyDiff(
|
fun <T> MutableList<T>.applyDiff(
|
||||||
source: Iterable<T>,
|
source: Iterable<T>,
|
||||||
strictComparison: Boolean = false
|
strictComparison: Boolean = false
|
||||||
) = calculateDiff(source, strictComparison).let {
|
): Diff<T> = calculateDiff(source, strictComparison).also {
|
||||||
for (i in it.removed.indices.sortedDescending()) {
|
for (i in it.removed.indices.sortedDescending()) {
|
||||||
removeAt(it.removed[i].index)
|
removeAt(it.removed[i].index)
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,61 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.children
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
|
||||||
|
fun findViewsByTag(viewGroup: ViewGroup, tag: Any?): List<View> {
|
||||||
|
return viewGroup.children.flatMap {
|
||||||
|
findViewsByTag(it, tag)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findViewsByTag(viewGroup: ViewGroup, key: Int, tag: Any?): List<View> {
|
||||||
|
return viewGroup.children.flatMap {
|
||||||
|
findViewsByTag(it, key, tag)
|
||||||
|
}.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findViewsByTag(view: View, tag: Any?): List<View> {
|
||||||
|
val result = mutableListOf<View>()
|
||||||
|
if (view.tag == tag) {
|
||||||
|
result.add(view)
|
||||||
|
}
|
||||||
|
if (view is ViewGroup) {
|
||||||
|
result.addAll(findViewsByTag(view, tag))
|
||||||
|
}
|
||||||
|
return result.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findViewsByTag(view: View, key: Int, tag: Any?): List<View> {
|
||||||
|
val result = mutableListOf<View>()
|
||||||
|
if (view.getTag(key) == tag) {
|
||||||
|
result.add(view)
|
||||||
|
}
|
||||||
|
if (view is ViewGroup) {
|
||||||
|
result.addAll(findViewsByTag(view, key, tag))
|
||||||
|
}
|
||||||
|
return result.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.findViewsByTag(tag: Any?) = rootView ?.let {
|
||||||
|
findViewsByTag(it, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.findViewsByTag(key: Int, tag: Any?) = rootView ?.let {
|
||||||
|
findViewsByTag(it, key, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.findViewsByTag(tag: Any?) = view ?.let {
|
||||||
|
findViewsByTag(it, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.findViewsByTag(key: Int, tag: Any?) = view ?.let {
|
||||||
|
findViewsByTag(it, key, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Fragment.findViewsByTagInActivity(tag: Any?) = activity ?.findViewsByTag(tag)
|
||||||
|
|
||||||
|
fun Fragment.findViewsByTagInActivity(key: Int, tag: Any?) = activity ?.findViewsByTag(key, tag)
|
@@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
val Activity.rootView: View?
|
||||||
|
get() = findViewById<View?>(android.R.id.content) ?.rootView ?: window.decorView.findViewById<View?>(android.R.id.content) ?.rootView
|
@@ -0,0 +1,39 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlin.js.JsName
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
inline fun <T, R> Flow<Flow<T>>.flatMap(
|
||||||
|
crossinline mapper: suspend (T) -> R
|
||||||
|
) = flow {
|
||||||
|
collect {
|
||||||
|
it.collect {
|
||||||
|
emit(mapper(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsName("flatMapIterable")
|
||||||
|
@JvmName("flatMapIterable")
|
||||||
|
inline fun <T, R> Flow<Iterable<T>>.flatMap(
|
||||||
|
crossinline mapper: suspend (T) -> R
|
||||||
|
) = map {
|
||||||
|
it.asFlow()
|
||||||
|
}.flatMap(mapper)
|
||||||
|
|
||||||
|
inline fun <T, R> Flow<Flow<T>>.flatMapNotNull(
|
||||||
|
crossinline mapper: suspend (T) -> R
|
||||||
|
) = flatMap(mapper).takeNotNull()
|
||||||
|
|
||||||
|
@JsName("flatMapNotNullIterable")
|
||||||
|
@JvmName("flatMapNotNullIterable")
|
||||||
|
inline fun <T, R> Flow<Iterable<T>>.flatMapNotNull(
|
||||||
|
crossinline mapper: suspend (T) -> R
|
||||||
|
) = flatMap(mapper).takeNotNull()
|
||||||
|
|
||||||
|
fun <T> Flow<Flow<T>>.flatten() = flatMap { it }
|
||||||
|
|
||||||
|
@JsName("flattenIterable")
|
||||||
|
@JvmName("flattenIterable")
|
||||||
|
fun <T> Flow<Iterable<T>>.flatten() = flatMap { it }
|
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
|
fun <T> Flow<T>.takeNotNull() = mapNotNull { it }
|
||||||
|
fun <T> Flow<T>.filterNotNull() = takeNotNull()
|
50
coroutines/src/main/kotlin/FlowOnHierarchyChangeListener.kt
Normal file
50
coroutines/src/main/kotlin/FlowOnHierarchyChangeListener.kt
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [kotlinx.coroutines.flow.Flow]-based [android.view.ViewGroup.OnHierarchyChangeListener]
|
||||||
|
*
|
||||||
|
* @param recursive If set, any call of [onChildViewAdded] will check if child [View] is [ViewGroup] and subscribe to this
|
||||||
|
* [ViewGroup] too
|
||||||
|
* @param [_onChildViewAdded] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewAdded] flow
|
||||||
|
* @param [_onChildViewRemoved] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewRemoved] flow
|
||||||
|
*/
|
||||||
|
class FlowOnHierarchyChangeListener(
|
||||||
|
private val recursive: Boolean = false,
|
||||||
|
private val _onChildViewAdded: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE),
|
||||||
|
private val _onChildViewRemoved: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
) : ViewGroup.OnHierarchyChangeListener {
|
||||||
|
val onChildViewAdded = _onChildViewAdded.asSharedFlow()
|
||||||
|
val onChildViewRemoved = _onChildViewRemoved.asSharedFlow()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will emit data into [onChildViewAdded] flow. If [recursive] is true and [child] is [ViewGroup] will also
|
||||||
|
* subscribe to [child] hierarchy changes.
|
||||||
|
*
|
||||||
|
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
|
||||||
|
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewAdded]. That is why its default extraBufferCapacity is
|
||||||
|
* [Int.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
override fun onChildViewAdded(parent: View, child: View) {
|
||||||
|
_onChildViewAdded.tryEmit(parent to child)
|
||||||
|
|
||||||
|
if (recursive && child is ViewGroup) {
|
||||||
|
child.setOnHierarchyChangeListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just emit data into [onChildViewRemoved]
|
||||||
|
*
|
||||||
|
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
|
||||||
|
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewRemoved]. That is why its default extraBufferCapacity is
|
||||||
|
* [Int.MAX_VALUE]
|
||||||
|
*/
|
||||||
|
override fun onChildViewRemoved(parent: View, child: View) {
|
||||||
|
_onChildViewRemoved.tryEmit(parent to child)
|
||||||
|
}
|
||||||
|
}
|
17
coroutines/src/main/kotlin/RecursiveHierarchySubscriber.kt
Normal file
17
coroutines/src/main/kotlin/RecursiveHierarchySubscriber.kt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.inmo.micro_utils.coroutines
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewGroup.OnHierarchyChangeListener
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use [ViewGroup.setOnHierarchyChangeListener] recursively for all available [ViewGroup]s starting with [this].
|
||||||
|
* This extension DO NOT guarantee that recursive subscription will happen after this method call
|
||||||
|
*/
|
||||||
|
fun ViewGroup.setOnHierarchyChangeListenerRecursively(
|
||||||
|
listener: OnHierarchyChangeListener
|
||||||
|
) {
|
||||||
|
setOnHierarchyChangeListener(listener)
|
||||||
|
(0 until childCount).forEach {
|
||||||
|
(getChildAt(it) as? ViewGroup) ?.setOnHierarchyChangeListenerRecursively(listener)
|
||||||
|
}
|
||||||
|
}
|
@@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.12.14
|
version=0.12.17
|
||||||
android_code_version=153
|
android_code_version=156
|
||||||
|
@@ -4,18 +4,18 @@ kt = "1.7.10"
|
|||||||
kt-serialization = "1.4.0"
|
kt-serialization = "1.4.0"
|
||||||
kt-coroutines = "1.6.4"
|
kt-coroutines = "1.6.4"
|
||||||
|
|
||||||
jb-compose = "1.2.0-alpha01-dev774"
|
jb-compose = "1.2.0-beta02"
|
||||||
jb-exposed = "0.39.2"
|
jb-exposed = "0.39.2"
|
||||||
jb-dokka = "1.7.10"
|
jb-dokka = "1.7.10"
|
||||||
|
|
||||||
klock = "3.1.0"
|
klock = "3.1.0"
|
||||||
uuid = "0.5.0"
|
uuid = "0.5.0"
|
||||||
|
|
||||||
ktor = "2.1.1"
|
ktor = "2.1.2"
|
||||||
|
|
||||||
gh-release = "2.4.1"
|
gh-release = "2.4.1"
|
||||||
|
|
||||||
koin = "3.2.0"
|
koin = "3.2.2"
|
||||||
|
|
||||||
android-gradle = "7.2.2"
|
android-gradle = "7.2.2"
|
||||||
dexcount = "3.1.0"
|
dexcount = "3.1.0"
|
||||||
|
Reference in New Issue
Block a user