mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-19 07:19:22 +00:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
3347a6d189 | |||
47b3b42949 | |||
e985631621 | |||
e15034bfa4 | |||
e5dbcd25dd | |||
3714c02c12 | |||
fa636b4146 | |||
3d825aebc3 | |||
629884a396 | |||
e7a0fa4e8f | |||
1a41f37a9d | |||
07a65e0bb5 | |||
615f7f99c3 | |||
3de5558ed4 | |||
6bbe3a271f | |||
8bee29f683 | |||
c40f0fdcb9 | |||
30d1453a12 | |||
75fa88b00d | |||
0f817ad212 | |||
d7b46ae0d4 | |||
b6be14ecca | |||
33dbfc6f69 |
@@ -22,5 +22,6 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- stage: build
|
- stage: build
|
||||||
script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest
|
script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest
|
||||||
- state: test
|
# Tests are temporarily disabled on public travis due to the problems of launching
|
||||||
script: ./gradlew allTests
|
# - state: test
|
||||||
|
# script: ./gradlew allTests
|
||||||
|
40
CHANGELOG.md
40
CHANGELOG.md
@@ -1,5 +1,45 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.5
|
||||||
|
|
||||||
|
* `Android`
|
||||||
|
* `Alerts`
|
||||||
|
* `Common`
|
||||||
|
* Project has been created
|
||||||
|
* `RecyclerView`
|
||||||
|
* Project has been created
|
||||||
|
* `Common`
|
||||||
|
* Annotation `PreviewFeature` has been added
|
||||||
|
* `Android`
|
||||||
|
* Added tools to work with visibility in more comfortable way
|
||||||
|
* Added tools to work with disabled/enabled state in more comfortable way
|
||||||
|
* Added tools to work with expanded/collapsed state in more comfortable way (in preview mode)
|
||||||
|
|
||||||
|
## 0.4.4
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Klock`: `1.12.1` -> `2.0.0`
|
||||||
|
* `Commons`:
|
||||||
|
* Update left items functionality to include work with `GridLayoutManager`
|
||||||
|
* `Repos`:
|
||||||
|
* Add interface `VersionsRepo`
|
||||||
|
* Add default realization of `VersionsRepo` named `StandardVersionsRepo` which use `StandardVersionsRepoProxy`
|
||||||
|
to get access to some end-store
|
||||||
|
* Add default realization of `StandardVersionsRepoProxy` based on `KeyValue` repos
|
||||||
|
* Add realizations of `StandardVersionsRepoProxy` for exposed and android (`SQL` and `SharedPreferences`)
|
||||||
|
* `Commons`:
|
||||||
|
* In Android fully reworked transactions functions
|
||||||
|
* Now `DatabaseCoroutineContext` is a shortcut for `Dispatchers.IO`
|
||||||
|
|
||||||
|
## 0.4.3
|
||||||
|
|
||||||
|
* `Versions`:
|
||||||
|
* `Kotlin`: `1.4.10` -> `1.4.20`
|
||||||
|
* `Common`:
|
||||||
|
* Two new extensions for Android:
|
||||||
|
* `Resources#getSp`
|
||||||
|
* `Resources#getDp`
|
||||||
|
|
||||||
## 0.4.2
|
## 0.4.2
|
||||||
|
|
||||||
* `Repos`:
|
* `Repos`:
|
||||||
|
17
android/alerts/common/build.gradle
Normal file
17
android/alerts/common/build.gradle
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.library"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppAndroidProjectPresetPath"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api "androidx.appcompat:appcompat-resources:$appcompat_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
android/alerts/common/src/main/AndroidManifest.xml
Normal file
1
android/alerts/common/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.android.alerts.common"/>
|
@@ -0,0 +1,55 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.android.alerts.common
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
|
||||||
|
typealias AlertDialogCallback = (DialogInterface) -> Unit
|
||||||
|
|
||||||
|
inline fun Context.createAlertDialogTemplate(
|
||||||
|
title: String? = null,
|
||||||
|
positivePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<String, AlertDialogCallback?>? = null
|
||||||
|
): AlertDialog.Builder {
|
||||||
|
val builder = AlertDialog.Builder(this)
|
||||||
|
|
||||||
|
title ?.let {
|
||||||
|
builder.setTitle(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
positivePair ?. let {
|
||||||
|
builder.setPositiveButton(it.first) { di, _ -> it.second ?. invoke(di) }
|
||||||
|
}
|
||||||
|
negativePair ?. let {
|
||||||
|
builder.setNegativeButton(it.first) { di, _ -> it.second ?. invoke(di) }
|
||||||
|
}
|
||||||
|
neutralPair ?. let {
|
||||||
|
builder.setNeutralButton(it.first) { di, _ -> it.second ?. invoke(di) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Context.createAlertDialogTemplateWithResources(
|
||||||
|
title: Int? = null,
|
||||||
|
positivePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<Int, AlertDialogCallback?>? = null
|
||||||
|
): AlertDialog.Builder = createAlertDialogTemplate(
|
||||||
|
title ?.let { getString(it) },
|
||||||
|
positivePair ?.let { getString(it.first) to it.second },
|
||||||
|
neutralPair ?.let { getString(it.first) to it.second },
|
||||||
|
negativePair ?.let { getString(it.first) to it.second }
|
||||||
|
)
|
||||||
|
|
||||||
|
inline fun AlertDialog.setDismissChecker(noinline checker: () -> Boolean) : AlertDialog {
|
||||||
|
setOnDismissListener {
|
||||||
|
if (!checker()) {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.android.alerts.common
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
inline fun <T: View> Context.createCustomViewAlertDialog(
|
||||||
|
title: String? = null,
|
||||||
|
positivePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true,
|
||||||
|
viewCreator: (Context) -> T
|
||||||
|
): AlertDialog = createAlertDialogTemplate(
|
||||||
|
title, positivePair, neutralPair, negativePair
|
||||||
|
).apply {
|
||||||
|
setView(viewCreator(this@createCustomViewAlertDialog))
|
||||||
|
}.create().apply {
|
||||||
|
if (show) show()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T: View> Context.createCustomViewAlertDialogWithResources(
|
||||||
|
title: Int? = null,
|
||||||
|
positivePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true,
|
||||||
|
viewCreator: (Context) -> T
|
||||||
|
): AlertDialog = createCustomViewAlertDialog(
|
||||||
|
title ?.let { getString(it) },
|
||||||
|
positivePair ?.let { getString(it.first) to it.second },
|
||||||
|
neutralPair ?.let { getString(it.first) to it.second },
|
||||||
|
negativePair ?.let { getString(it.first) to it.second },
|
||||||
|
show,
|
||||||
|
viewCreator
|
||||||
|
)
|
@@ -0,0 +1,45 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.android.alerts.common
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
inline fun Context.createSimpleTextAlertDialog(
|
||||||
|
text: String,
|
||||||
|
title: String? = null,
|
||||||
|
positivePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<String, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true
|
||||||
|
): AlertDialog = createAlertDialogTemplate(
|
||||||
|
title,
|
||||||
|
positivePair,
|
||||||
|
neutralPair,
|
||||||
|
negativePair
|
||||||
|
).apply {
|
||||||
|
setMessage(text)
|
||||||
|
}.create().apply {
|
||||||
|
if (show) {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun Context.createSimpleTextAlertDialog(
|
||||||
|
@StringRes
|
||||||
|
text: Int,
|
||||||
|
@StringRes
|
||||||
|
title: Int? = null,
|
||||||
|
positivePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true
|
||||||
|
): AlertDialog = createSimpleTextAlertDialog(
|
||||||
|
getString(text),
|
||||||
|
title ?.let { getString(it) },
|
||||||
|
positivePair ?.let { getString(it.first) to it.second },
|
||||||
|
neutralPair ?.let { getString(it.first) to it.second },
|
||||||
|
negativePair ?.let { getString(it.first) to it.second },
|
||||||
|
show
|
||||||
|
)
|
18
android/alerts/recyclerview/build.gradle
Normal file
18
android/alerts/recyclerview/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.library"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppAndroidProjectPresetPath"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api internalProject("micro_utils.android.alerts.common")
|
||||||
|
api internalProject("micro_utils.android.recyclerview")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
android/alerts/recyclerview/src/main/AndroidManifest.xml
Normal file
1
android/alerts/recyclerview/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.android.alerts.recyclerview"/>
|
@@ -0,0 +1,65 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.android.alerts.recyclerview
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import dev.inmo.micro_utils.android.alerts.common.AlertDialogCallback
|
||||||
|
import dev.inmo.micro_utils.android.recyclerview.*
|
||||||
|
|
||||||
|
data class AlertAction(
|
||||||
|
val title: String,
|
||||||
|
val callback: (DialogInterface) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
private class ActionViewHolder(
|
||||||
|
container: ViewGroup, dialogInterfaceGetter: () -> DialogInterface
|
||||||
|
) : AbstractStandardViewHolder<AlertAction>(container, android.R.layout.simple_list_item_1) {
|
||||||
|
private lateinit var action: AlertAction
|
||||||
|
private val textView: TextView
|
||||||
|
get() = itemView.findViewById(android.R.id.text1)
|
||||||
|
|
||||||
|
init {
|
||||||
|
itemView.setOnClickListener {
|
||||||
|
action.callback(dialogInterfaceGetter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(item: AlertAction) {
|
||||||
|
action = item
|
||||||
|
textView.text = item.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ActionsRecyclerViewAdapter(
|
||||||
|
data: List<AlertAction>,
|
||||||
|
private val dialogInterfaceGetter: () -> DialogInterface
|
||||||
|
) : RecyclerViewAdapter<AlertAction>(data) {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder<AlertAction> = ActionViewHolder(
|
||||||
|
parent, dialogInterfaceGetter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.createActionsAlertDialog(
|
||||||
|
actions: List<AlertAction>,
|
||||||
|
title: Int? = null,
|
||||||
|
positivePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true
|
||||||
|
): AlertDialog {
|
||||||
|
lateinit var dialogInterface: DialogInterface
|
||||||
|
|
||||||
|
return createRecyclerViewDialog(
|
||||||
|
title, positivePair, neutralPair, negativePair, show
|
||||||
|
) {
|
||||||
|
ActionsRecyclerViewAdapter(
|
||||||
|
actions
|
||||||
|
) {
|
||||||
|
dialogInterface
|
||||||
|
}
|
||||||
|
}.also { dialogInterface = it }
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
package dev.inmo.micro_utils.android.alerts.recyclerview
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dev.inmo.micro_utils.android.alerts.common.AlertDialogCallback
|
||||||
|
import dev.inmo.micro_utils.android.alerts.common.createCustomViewAlertDialogWithResources
|
||||||
|
|
||||||
|
fun Context.createRecyclerViewDialog(
|
||||||
|
title: Int? = null,
|
||||||
|
positivePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
neutralPair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
negativePair: Pair<Int, AlertDialogCallback?>? = null,
|
||||||
|
show: Boolean = true,
|
||||||
|
layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this),
|
||||||
|
marginOfRecyclerView: Int = 8, // dp
|
||||||
|
recyclerViewSetUp: RecyclerView.() -> Unit = {},
|
||||||
|
adapterFactory: () -> RecyclerView.Adapter<*>
|
||||||
|
): AlertDialog {
|
||||||
|
val recyclerView = RecyclerView(this).apply {
|
||||||
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply {
|
||||||
|
setMargins(marginOfRecyclerView, marginOfRecyclerView, marginOfRecyclerView, marginOfRecyclerView)
|
||||||
|
}
|
||||||
|
this.layoutManager = layoutManager
|
||||||
|
adapter = adapterFactory()
|
||||||
|
recyclerViewSetUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
return createCustomViewAlertDialogWithResources(
|
||||||
|
title,
|
||||||
|
positivePair,
|
||||||
|
neutralPair,
|
||||||
|
negativePair,
|
||||||
|
show
|
||||||
|
) {
|
||||||
|
recyclerView
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppAndroidProjectPresetPath"
|
apply from: "$mppAndroidProjectPresetPath"
|
||||||
|
@@ -1,14 +1,20 @@
|
|||||||
package dev.inmo.micro_utils.android.recyclerview
|
package dev.inmo.micro_utils.android.recyclerview
|
||||||
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun RecyclerView.LayoutManager.findLastVisibleItemPositionGetter(): (() -> Int)? = when (this) {
|
||||||
|
is LinearLayoutManager -> ::findLastVisibleItemPosition
|
||||||
|
is GridLayoutManager -> ::findLastVisibleItemPosition
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
fun RecyclerView.lastVisibleItemFlow(
|
fun RecyclerView.lastVisibleItemFlow(
|
||||||
completingScope: CoroutineScope
|
completingScope: CoroutineScope
|
||||||
): Flow<Int> {
|
): Flow<Int> {
|
||||||
val lastVisibleElementFun: () -> Int = (layoutManager as? LinearLayoutManager) ?.let { it::findLastVisibleItemPosition } ?: error("Currently supported only linear layout manager")
|
val lastVisibleElementFun: () -> Int = layoutManager ?.findLastVisibleItemPositionGetter() ?: error("Currently supported only linear and grid layout manager")
|
||||||
val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun())
|
val lastVisibleFlow = MutableStateFlow(lastVisibleElementFun())
|
||||||
addOnScrollListener(
|
addOnScrollListener(
|
||||||
object : RecyclerView.OnScrollListener() {
|
object : RecyclerView.OnScrollListener() {
|
||||||
|
@@ -8,10 +8,9 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
|
|
||||||
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
||||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
||||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
|
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
@RequiresOptIn(
|
||||||
|
"It is possible, that behaviour of this thing will be changed or removed in future releases",
|
||||||
|
RequiresOptIn.Level.WARNING
|
||||||
|
)
|
||||||
|
@Target(
|
||||||
|
AnnotationTarget.CLASS,
|
||||||
|
AnnotationTarget.CONSTRUCTOR,
|
||||||
|
AnnotationTarget.FIELD,
|
||||||
|
AnnotationTarget.PROPERTY,
|
||||||
|
AnnotationTarget.PROPERTY_GETTER,
|
||||||
|
AnnotationTarget.PROPERTY_SETTER,
|
||||||
|
AnnotationTarget.FUNCTION,
|
||||||
|
AnnotationTarget.TYPE,
|
||||||
|
AnnotationTarget.TYPEALIAS,
|
||||||
|
AnnotationTarget.TYPE_PARAMETER
|
||||||
|
)
|
||||||
|
annotation class PreviewFeature
|
@@ -0,0 +1,13 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
|
||||||
|
inline fun Resources.getSp(
|
||||||
|
resId: Int
|
||||||
|
) = getDimension(resId) / displayMetrics.scaledDensity
|
||||||
|
|
||||||
|
inline fun Resources.getDp(
|
||||||
|
resId: Int
|
||||||
|
) = getDimension(resId) * displayMetrics.density
|
@@ -0,0 +1,76 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.Transformation
|
||||||
|
|
||||||
|
@PreviewFeature
|
||||||
|
fun View.expand(
|
||||||
|
duration: Long = 500,
|
||||||
|
targetWidth: Int = ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
targetHeight: Int = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
) {
|
||||||
|
measure(targetWidth, targetHeight)
|
||||||
|
val measuredHeight: Int = measuredHeight
|
||||||
|
layoutParams.height = 0
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
val a: Animation = object : Animation() {
|
||||||
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
|
||||||
|
super.applyTransformation(interpolatedTime, t)
|
||||||
|
layoutParams.height = if (interpolatedTime == 1f) targetHeight else (measuredHeight * interpolatedTime).toInt()
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willChangeBounds(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.duration = duration
|
||||||
|
|
||||||
|
startAnimation(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewFeature
|
||||||
|
fun View.collapse(duration: Long = 500) {
|
||||||
|
val initialHeight: Int = measuredHeight
|
||||||
|
val a: Animation = object : Animation() {
|
||||||
|
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
|
||||||
|
if (interpolatedTime == 1f) {
|
||||||
|
visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
layoutParams.height = initialHeight - (initialHeight * interpolatedTime).toInt()
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willChangeBounds(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.duration = duration
|
||||||
|
|
||||||
|
startAnimation(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewFeature
|
||||||
|
inline val View.isCollapsed
|
||||||
|
get() = visibility == View.GONE
|
||||||
|
|
||||||
|
@PreviewFeature
|
||||||
|
inline val View.isExpanded
|
||||||
|
get() = !isCollapsed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true in case of expanding
|
||||||
|
*/
|
||||||
|
@PreviewFeature
|
||||||
|
fun View.toggleExpandState(duration: Long = 500): Boolean = if (isCollapsed) {
|
||||||
|
expand(duration)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
collapse(duration)
|
||||||
|
false
|
||||||
|
}
|
@@ -0,0 +1,34 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
|
||||||
|
inline val View.enabled
|
||||||
|
get() = isEnabled
|
||||||
|
|
||||||
|
inline val View.disabled
|
||||||
|
get() = !enabled
|
||||||
|
|
||||||
|
fun View.disable() {
|
||||||
|
if (this is ViewGroup) {
|
||||||
|
(0 until childCount).forEach { getChildAt(it).disable() }
|
||||||
|
}
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.enable() {
|
||||||
|
if (this is ViewGroup) {
|
||||||
|
(0 until childCount).forEach { getChildAt(it).enable() }
|
||||||
|
}
|
||||||
|
isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.toggleEnabledState(enabled: Boolean) {
|
||||||
|
if (enabled) {
|
||||||
|
enable()
|
||||||
|
} else {
|
||||||
|
disable()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
inline val View.gone
|
||||||
|
get() = visibility == View.GONE
|
||||||
|
inline fun View.gone() {
|
||||||
|
visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
inline val View.hidden
|
||||||
|
get() = visibility == View.INVISIBLE
|
||||||
|
inline fun View.hide() {
|
||||||
|
visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
inline val View.shown
|
||||||
|
get() = visibility == View.VISIBLE
|
||||||
|
inline fun View.show() {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.toggleVisibility(goneOnHide: Boolean = true) {
|
||||||
|
if (isShown) {
|
||||||
|
if (goneOnHide) {
|
||||||
|
gone()
|
||||||
|
} else {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
id "org.jetbrains.dokka"
|
id "org.jetbrains.dokka"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,18 +5,17 @@ kotlin.incremental=true
|
|||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
org.gradle.jvmargs=-Xmx3072m
|
org.gradle.jvmargs=-Xmx2048m
|
||||||
|
|
||||||
kotlin_version=1.4.10
|
kotlin_version=1.4.20
|
||||||
kotlin_coroutines_version=1.4.1
|
kotlin_coroutines_version=1.4.1
|
||||||
kotlin_serialisation_core_version=1.0.1
|
kotlin_serialisation_core_version=1.0.1
|
||||||
kotlin_exposed_version=0.28.1
|
kotlin_exposed_version=0.28.1
|
||||||
|
|
||||||
ktor_version=1.4.2
|
ktor_version=1.4.2
|
||||||
|
|
||||||
klockVersion=1.12.1
|
klockVersion=2.0.0
|
||||||
|
|
||||||
gradle_bintray_plugin_version=1.8.5
|
|
||||||
github_release_plugin_version=2.2.12
|
github_release_plugin_version=2.2.12
|
||||||
|
|
||||||
uuidVersion=0.2.2
|
uuidVersion=0.2.2
|
||||||
@@ -25,6 +24,7 @@ uuidVersion=0.2.2
|
|||||||
|
|
||||||
core_ktx_version=1.3.2
|
core_ktx_version=1.3.2
|
||||||
androidx_recycler_version=1.1.0
|
androidx_recycler_version=1.1.0
|
||||||
|
appcompat_version=1.2.0
|
||||||
|
|
||||||
android_minSdkVersion=19
|
android_minSdkVersion=19
|
||||||
android_compileSdkVersion=30
|
android_compileSdkVersion=30
|
||||||
@@ -41,5 +41,5 @@ dokka_version=1.4.0
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.4.2
|
version=0.4.5
|
||||||
android_code_version=6
|
android_code_version=9
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.versions
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.set
|
||||||
|
|
||||||
|
class KeyValueBasedVersionsRepoProxy<T>(
|
||||||
|
private val keyValueStore: StandardKeyValueRepo<String, Int>,
|
||||||
|
override val database: T
|
||||||
|
) : StandardVersionsRepoProxy<T> {
|
||||||
|
override suspend fun getTableVersion(tableName: String): Int? = keyValueStore.get(tableName)
|
||||||
|
|
||||||
|
override suspend fun updateTableVersion(tableName: String, version: Int) { keyValueStore.set(tableName, version) }
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.versions
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.Repo
|
||||||
|
|
||||||
|
interface StandardVersionsRepoProxy<T> : Repo {
|
||||||
|
val database: T
|
||||||
|
|
||||||
|
suspend fun getTableVersion(tableName: String): Int?
|
||||||
|
suspend fun updateTableVersion(tableName: String, version: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardVersionsRepo<T>(
|
||||||
|
private val proxy: StandardVersionsRepoProxy<T>
|
||||||
|
) : VersionsRepo<T> {
|
||||||
|
override suspend fun setTableVersion(
|
||||||
|
tableName: String,
|
||||||
|
version: Int,
|
||||||
|
onCreate: suspend T.() -> Unit,
|
||||||
|
onUpdate: suspend T.(from: Int, to: Int) -> Unit
|
||||||
|
) {
|
||||||
|
var savedVersion = proxy.getTableVersion(tableName)
|
||||||
|
if (savedVersion == null) {
|
||||||
|
proxy.database.onCreate()
|
||||||
|
proxy.updateTableVersion(tableName, version)
|
||||||
|
} else {
|
||||||
|
while (savedVersion != null && savedVersion < version) {
|
||||||
|
val newVersion = savedVersion + 1
|
||||||
|
|
||||||
|
proxy.database.onUpdate(savedVersion, newVersion)
|
||||||
|
|
||||||
|
proxy.updateTableVersion(tableName, newVersion)
|
||||||
|
savedVersion = newVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.versions
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.Repo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface has been created due to requirement to work with different versions of databases and make some
|
||||||
|
* migrations between versions
|
||||||
|
*
|
||||||
|
* @param T It is a type of database, which will be used by this repo to retrieve current table version and update it
|
||||||
|
*/
|
||||||
|
interface VersionsRepo<T> : Repo {
|
||||||
|
/**
|
||||||
|
* By default, instance of this interface will check that version of table with name [tableName] is less than
|
||||||
|
* [version] or is absent
|
||||||
|
*
|
||||||
|
* * In case if [tableName] didn't found, will be called [onCreate] and version of table will be set up to [version]
|
||||||
|
* * In case if [tableName] have version less than parameter [version], it will increase version one-by-one
|
||||||
|
* until database version will be equal to [version]
|
||||||
|
*
|
||||||
|
* @param version Current version of table
|
||||||
|
* @param onCreate This callback will be called in case when table have no information about table
|
||||||
|
* @param onUpdate This callback will be called after **iterative** changing of version. It is expected that parameter
|
||||||
|
* "to" will always be greater than "from"
|
||||||
|
*/
|
||||||
|
suspend fun setTableVersion(
|
||||||
|
tableName: String,
|
||||||
|
version: Int,
|
||||||
|
onCreate: suspend T.() -> Unit = {},
|
||||||
|
onUpdate: suspend T.(from: Int, to: Int) -> Unit = { _, _ ->}
|
||||||
|
)
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
package dev.inmo.micro_utils.repos
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
import kotlinx.coroutines.newSingleThreadContext
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
val DatabaseCoroutineContext: CoroutineContext = newSingleThreadContext("db-context")
|
val DatabaseCoroutineContext: CoroutineContext = Dispatchers.IO
|
||||||
|
@@ -2,15 +2,58 @@ package dev.inmo.micro_utils.repos
|
|||||||
|
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
|
private object ContextsPool {
|
||||||
|
private val contexts = mutableListOf<CoroutineContext>()
|
||||||
|
private val mutex = Mutex(locked = false)
|
||||||
|
private val freeContexts = mutableListOf<CoroutineContext>()
|
||||||
|
|
||||||
|
suspend fun acquireContext(): CoroutineContext {
|
||||||
|
return mutex.withLock {
|
||||||
|
freeContexts.removeFirstOrNull() ?: Executors.newSingleThreadExecutor().asCoroutineDispatcher().also {
|
||||||
|
contexts.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun freeContext(context: CoroutineContext) {
|
||||||
|
return mutex.withLock {
|
||||||
|
if (context in contexts && context !in freeContexts) {
|
||||||
|
freeContexts.add(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> use(block: suspend (CoroutineContext) -> T): T = acquireContext().let {
|
||||||
|
try {
|
||||||
|
block(it)
|
||||||
|
} finally {
|
||||||
|
freeContext(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransactionContext(
|
||||||
|
val databaseContext: CoroutineContext
|
||||||
|
): CoroutineContext.Element {
|
||||||
|
override val key: CoroutineContext.Key<TransactionContext> = TransactionContext
|
||||||
|
|
||||||
|
companion object : CoroutineContext.Key<TransactionContext>
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
return withContext(DatabaseCoroutineContext) {
|
return coroutineContext[TransactionContext] ?.let {
|
||||||
when {
|
withContext(it.databaseContext) {
|
||||||
inTransaction() -> {
|
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
else -> {
|
} ?: ContextsPool.use { context ->
|
||||||
|
withContext(TransactionContext(context) + context) {
|
||||||
beginTransaction()
|
beginTransaction()
|
||||||
safely(
|
safely(
|
||||||
{
|
{
|
||||||
@@ -25,7 +68,6 @@ suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T {
|
inline fun <T> SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
@@ -5,6 +5,7 @@ import android.database.DatabaseErrorHandler
|
|||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
import dev.inmo.micro_utils.coroutines.safely
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
|
import dev.inmo.micro_utils.repos.versions.*
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
@@ -36,6 +37,9 @@ class StandardSQLHelper(
|
|||||||
|
|
||||||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
|
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
|
||||||
}
|
}
|
||||||
|
val versionsRepo: VersionsRepo<SQLiteOpenHelper> by lazy {
|
||||||
|
StandardVersionsRepo(AndroidSQLStandardVersionsRepoProxy(sqlOpenHelper))
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)
|
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)
|
||||||
|
|
||||||
|
@@ -0,0 +1,63 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.versions
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will create [VersionsRepo] based on [SQLiteOpenHelper] with table inside of [database]
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun versionsRepo(database: SQLiteOpenHelper): VersionsRepo<SQLiteOpenHelper> = StandardVersionsRepo(
|
||||||
|
AndroidSQLStandardVersionsRepoProxy(database)
|
||||||
|
)
|
||||||
|
|
||||||
|
class AndroidSQLStandardVersionsRepoProxy(
|
||||||
|
override val database: SQLiteOpenHelper
|
||||||
|
) : StandardVersionsRepoProxy<SQLiteOpenHelper> {
|
||||||
|
private val tableName: String = "AndroidSQLStandardVersionsRepo"
|
||||||
|
private val tableNameColumnName = "tableName"
|
||||||
|
private val tableVersionColumnName = "version"
|
||||||
|
|
||||||
|
init {
|
||||||
|
runBlocking(DatabaseCoroutineContext) {
|
||||||
|
database.writableTransaction {
|
||||||
|
createTable(
|
||||||
|
tableName,
|
||||||
|
tableNameColumnName to ColumnType.Text.NOT_NULLABLE,
|
||||||
|
tableVersionColumnName to ColumnType.Numeric.INTEGER()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTableVersion(table: String): Int? = database.writableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
selection = "$tableNameColumnName=?",
|
||||||
|
selectionArgs = arrayOf(table),
|
||||||
|
limit = limitClause(1)
|
||||||
|
).use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
it.getInt(tableVersionColumnName)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateTableVersion(table: String, version: Int) {
|
||||||
|
database.writableTransaction {
|
||||||
|
val updated = update(
|
||||||
|
tableName,
|
||||||
|
contentValuesOf(tableVersionColumnName to version),
|
||||||
|
"$tableNameColumnName=?",
|
||||||
|
arrayOf(table)
|
||||||
|
) > 0
|
||||||
|
if (!updated) {
|
||||||
|
insert(tableName, null, contentValuesOf(tableNameColumnName to table, tableVersionColumnName to version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.repos.versions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
import dev.inmo.micro_utils.repos.keyvalue.keyValueStore
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will create [VersionsRepo] based on [T], but versions will be stored in [StandardKeyValueRepo]
|
||||||
|
*
|
||||||
|
* @receiver Will be used to create [KeyValueBasedVersionsRepoProxy] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||||
|
*
|
||||||
|
* @see [KeyValueBasedVersionsRepoProxy]
|
||||||
|
* @see [keyValueStore]
|
||||||
|
*/
|
||||||
|
inline fun <T> Context.versionsKeyValueRepo(
|
||||||
|
database: T
|
||||||
|
): VersionsRepo<T> = StandardVersionsRepo(
|
||||||
|
KeyValueBasedVersionsRepoProxy(
|
||||||
|
keyValueStore("SPVersionsRepo"),
|
||||||
|
database
|
||||||
|
)
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
|
||||||
|
*
|
||||||
|
* @receiver Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||||
|
*
|
||||||
|
* @see [keyValueStore]
|
||||||
|
*/
|
||||||
|
inline fun Context.versionsKeyValueRepoForSQL(
|
||||||
|
database: SQLiteOpenHelper
|
||||||
|
) = versionsKeyValueRepo(database)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
|
||||||
|
*
|
||||||
|
* @param context Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
|
||||||
|
*
|
||||||
|
* @see [keyValueStore]
|
||||||
|
*/
|
||||||
|
inline fun versionsRepo(
|
||||||
|
context: Context,
|
||||||
|
database: SQLiteOpenHelper
|
||||||
|
) = context.versionsKeyValueRepoForSQL(database)
|
@@ -0,0 +1,48 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.versions
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.initTable
|
||||||
|
import dev.inmo.micro_utils.repos.versions.*
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as
|
||||||
|
* [StandardVersionsRepoProxy]
|
||||||
|
*/
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVersionsRepo(
|
||||||
|
ExposedStandardVersionsRepoProxy(database)
|
||||||
|
)
|
||||||
|
|
||||||
|
class ExposedStandardVersionsRepoProxy(
|
||||||
|
override val database: Database
|
||||||
|
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
|
||||||
|
private val tableNameColumn = text("tableName")
|
||||||
|
private val tableVersionColumn = integer("tableName")
|
||||||
|
|
||||||
|
init {
|
||||||
|
initTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getTableVersion(tableName: String): Int? = transaction(database) {
|
||||||
|
select { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateTableVersion(tableName: String, version: Int) {
|
||||||
|
transaction(database) {
|
||||||
|
val updated = update(
|
||||||
|
{ tableNameColumn.eq(tableName) }
|
||||||
|
) {
|
||||||
|
it[tableVersionColumn] = version
|
||||||
|
} > 0
|
||||||
|
if (!updated) {
|
||||||
|
insert {
|
||||||
|
it[tableNameColumn] = tableName
|
||||||
|
it[tableVersionColumn] = version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -2,7 +2,6 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
id "com.android.library"
|
id "com.android.library"
|
||||||
id "kotlin-android-extensions"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
@@ -18,6 +18,8 @@ String[] includes = [
|
|||||||
":ktor:client",
|
":ktor:client",
|
||||||
":coroutines",
|
":coroutines",
|
||||||
":android:recyclerview",
|
":android:recyclerview",
|
||||||
|
":android:alerts:common",
|
||||||
|
":android:alerts:recyclerview",
|
||||||
|
|
||||||
":dokka"
|
":dokka"
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user