Merge pull request #31 from InsanusMokrassar/0.4.12

0.4.12
This commit is contained in:
InsanusMokrassar 2020-12-14 21:11:57 +06:00 committed by GitHub
commit bd396959a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 219 additions and 4 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## 0.4.12
* `Coroutines`
* `JVM`
* Update `launchSynchronously` signature
* `Selector`
* Project created
## 0.4.11
* `Common`

View File

@ -17,6 +17,7 @@ You always can look at the <a href="https://github.com/InsanusMokrassar/MicroUti
## Projects
* `common` contains common tools for platform which usually are absent out-of-the-box when you starting project
* `selector` contains tools to use `Selector` interface with things like `RecyclerView` in android or other selection needs
* `coroutines` is a module for `Kotlin Coroutines` with different things like subscribing on flows (`onEach` + `launchIn` shortcut :) )
* `ktor` is a set of modules for `client`s and `server`s
* `mime_types` is NOT lightweight set of `MimeType`s with a lot of different objected and serializable (with `Kotlin Serialization`) mime types

View File

@ -2,12 +2,12 @@ package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
fun <T> launchSynchronously(scope: CoroutineScope = CoroutineScope(Dispatchers.Default), block: suspend CoroutineScope.() -> T): T {
fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T): T {
var throwable: Throwable? = null
var result: T? = null
val objectToSynchronize = java.lang.Object()
val launchCallback = {
scope.launch {
launch {
safely(
{
throwable = it
@ -26,3 +26,5 @@ fun <T> launchSynchronously(scope: CoroutineScope = CoroutineScope(Dispatchers.D
}
throw throwable ?: return result!!
}
fun <T> launchSynchronously(block: suspend CoroutineScope.() -> T): T = CoroutineScope(Dispatchers.Default).launchSynchronously(block)

View File

@ -40,5 +40,5 @@ dokka_version=1.4.20
# Project data
group=dev.inmo
version=0.4.11
android_code_version=15
version=0.4.12
android_code_version=16

View File

@ -0,0 +1,17 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
}
}
}
}

View File

@ -0,0 +1,168 @@
package dev.inmo.micro_utils.selector
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
/**
* Unified interface which can be used in any system which require some selection functionality
*/
interface Selector<T> {
val selectedItems: List<T>
val itemSelected: SharedFlow<T>
val itemUnselected: SharedFlow<T>
suspend fun toggleSelection(element: T)
suspend fun forceSelect(element: T)
suspend fun forceDeselect(element: T)
suspend fun clearSelection()
}
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> Selector<T>.contains(element: T) = selectedItems.contains(element)
@Suppress("NOTHING_TO_INLINE")
inline fun <T> Selector<T>.nothingSelected(): Boolean = selectedItems.isEmpty()
suspend inline fun <T> Selector<T>.toggleSelection(elements: List<T>) = elements.forEach { toggleSelection(it) }
suspend inline fun <T> Selector<T>.forceSelect(elements: List<T>) = elements.forEach { forceSelect(it) }
suspend inline fun <T> Selector<T>.forceDeselect(elements: List<T>) = elements.forEach { forceDeselect(it) }
suspend inline fun <T> Selector<T>.toggleSelection(firstElement: T, vararg elements: T) = toggleSelection(listOf(firstElement) + elements.toList())
suspend inline fun <T> Selector<T>.forceSelect(firstElement: T, vararg elements: T) = forceSelect(listOf(firstElement) + elements.toList())
suspend inline fun <T> Selector<T>.forceDeselect(firstElement: T, vararg elements: T) = forceDeselect(listOf(firstElement) + elements.toList())
/**
* Realization of [Selector] with one or without selected element. This realization will always have empty
* [selectedItems] when nothing selected and one element in [selectedItems] when something selected. Contains
* [selectedItem] value for simple access to currently selected item.
*
* On calling of [toggleSelection] previous selection will be erased and [itemUnselected] will emit this element.
*
* @param safeChanges Set to false to disable using of [mutex] for synchronizing changes on [toggleSelection]
*/
class SingleSelector<T>(
selectedItem: T? = null,
safeChanges: Boolean = true
) : Selector<T> {
var selectedItem: T? = selectedItem
private set
override val selectedItems: List<T>
get() = selectedItem ?.let { listOf(it) } ?: emptyList()
private val _itemSelected = MutableSharedFlow<T>()
override val itemSelected: SharedFlow<T> = _itemSelected.asSharedFlow()
private val _itemUnselected = MutableSharedFlow<T>()
override val itemUnselected: SharedFlow<T> = _itemUnselected.asSharedFlow()
private val mutex = if (safeChanges) {
Mutex()
} else {
null
}
override suspend fun forceDeselect(element: T) {
mutex ?.lock()
if (selectedItem == element) {
selectedItem = null
_itemUnselected.emit(element)
}
mutex ?.unlock()
}
override suspend fun forceSelect(element: T) {
mutex ?.lock()
if (selectedItem != element) {
selectedItem = element
_itemSelected.emit(element)
}
mutex ?.unlock()
}
override suspend fun toggleSelection(element: T) {
mutex ?.lock()
if (selectedItem == element) {
selectedItem = null
_itemUnselected.emit(element)
} else {
val previouslySelected = selectedItem
selectedItem = null
if (previouslySelected != null) {
_itemUnselected.emit(previouslySelected)
}
selectedItem = element
_itemSelected.emit(element)
}
mutex ?.unlock()
}
override suspend fun clearSelection() {
selectedItem ?.let { forceDeselect(it) }
}
}
/**
* Realization of [Selector] with multiple selected elements. On calling of [toggleSelection] this realization will select passed element OR deselect it if it is already in
* [selectedItems]
*
* @param safeChanges Set to false to disable using of [mutex] for synchronizing changes on [toggleSelection]
*/
class MultipleSelector<T>(
selectedItems: List<T> = emptyList(),
safeChanges: Boolean = true
) : Selector<T> {
private val _selectedItems: MutableList<T> = selectedItems.toMutableList()
override val selectedItems: List<T> = _selectedItems
private val _itemSelected = MutableSharedFlow<T>()
override val itemSelected: SharedFlow<T> = _itemSelected.asSharedFlow()
private val _itemUnselected = MutableSharedFlow<T>()
override val itemUnselected: SharedFlow<T> = _itemUnselected.asSharedFlow()
private val mutex = if (safeChanges) {
Mutex()
} else {
null
}
override suspend fun forceDeselect(element: T) {
mutex ?.lock()
if (_selectedItems.remove(element)) {
_itemUnselected.emit(element)
}
mutex ?.unlock()
}
override suspend fun forceSelect(element: T) {
mutex ?.lock()
if (element !in _selectedItems && _selectedItems.add(element)) {
_itemSelected.emit(element)
}
mutex ?.unlock()
}
override suspend fun toggleSelection(element: T) {
mutex ?.lock()
if (_selectedItems.remove(element)) {
_itemUnselected.emit(element)
} else {
_selectedItems.add(element)
_itemSelected.emit(element)
}
mutex ?.unlock()
}
override suspend fun clearSelection() {
mutex ?.lock()
val preSelectedItems = _selectedItems.toList()
_selectedItems.clear()
preSelectedItems.forEach { _itemUnselected.emit(it) }
mutex ?.unlock()
}
}
@Suppress("FunctionName", "NOTHING_TO_INLINE")
inline fun <T> Selector(
multiple: Boolean,
safeChanges: Boolean = true
): Selector<T> = if (multiple) {
MultipleSelector(safeChanges = safeChanges)
} else {
SingleSelector(safeChanges = safeChanges)
}

View File

@ -0,0 +1,17 @@
package dev.inmo.micro_utils.selector
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
/**
* @return Returned [SharedFlow] will emit true when [element] has been selected in [this] [Selector] and will emit
* false when this [element] was deselected
*
* @see [Selector]
* @see [Selector.itemSelected]
* @see [Selector.itemUnselected]
*/
fun <T> Selector<T>.itemSelectionFlow(element: T, scope: CoroutineScope): SharedFlow<Boolean> = MutableSharedFlow<Boolean>().apply {
itemSelected.onEach { if (it == element) emit(true) }.launchIn(scope)
itemUnselected.onEach { if (it == element) emit(false) }.launchIn(scope)
}.asSharedFlow()

View File

@ -0,0 +1 @@
<manifest package="dev.inmo.micro_utils.selector"/>

View File

@ -2,6 +2,7 @@ rootProject.name='micro_utils'
String[] includes = [
":common",
":selector:common",
":pagination:common",
":pagination:exposed",
":pagination:ktor:common",