diff --git a/CHANGELOG.md b/CHANGELOG.md index 9664e270945..9aae3ae9c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.12.16 +* `Coroutines`: + * `Android`: + * Add class `FlowOnHierarchyChangeListener` + * Add `ViewGroup#setOnHierarchyChangeListenerRecursively(OnHierarchyChangeListener)` + ## 0.12.15 * `Common`: diff --git a/coroutines/src/main/kotlin/FlowOnHierarchyChangeListener.kt b/coroutines/src/main/kotlin/FlowOnHierarchyChangeListener.kt new file mode 100644 index 00000000000..f05056bcd9a --- /dev/null +++ b/coroutines/src/main/kotlin/FlowOnHierarchyChangeListener.kt @@ -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> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE), + private val _onChildViewRemoved: MutableSharedFlow> = 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) + } +} diff --git a/coroutines/src/main/kotlin/RecursiveHierarchySubscriber.kt b/coroutines/src/main/kotlin/RecursiveHierarchySubscriber.kt new file mode 100644 index 00000000000..c991f8d50c8 --- /dev/null +++ b/coroutines/src/main/kotlin/RecursiveHierarchySubscriber.kt @@ -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) ?.setOnHierarchyChangeListener(listener) + } +}