mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-22 16:23:50 +00:00
add android tools
This commit is contained in:
parent
97dadf517a
commit
5ce71ee6f6
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
common/src/main/AndroidManifest.xml
Normal file
1
common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.common"/>
|
@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST", "SimplifiableCall")
|
||||||
|
inline fun <T, R> Iterable<T>.mapNotNullA(transform: (T) -> R?): List<R> = map(transform).filter { it != null } as List<R>
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST", "SimplifiableCall")
|
||||||
|
inline fun <T, R> Array<T>.mapNotNullA(mapper: (T) -> R?): List<R> = map(mapper).filter { it != null } as List<R>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
coroutines/src/main/AndroidManifest.xml
Normal file
1
coroutines/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.coroutines"/>
|
31
defaultAndroidSettings
Normal file
31
defaultAndroidSettings
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
android {
|
||||||
|
compileSdkVersion "$android_compileSdkVersion".toInteger()
|
||||||
|
buildToolsVersion "$android_buildToolsVersion"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion "$android_minSdkVersion".toInteger()
|
||||||
|
targetSdkVersion "$android_compileSdkVersion".toInteger()
|
||||||
|
versionCode "${android_code_version}".toInteger()
|
||||||
|
versionName "$version"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module'
|
||||||
|
exclude 'META-INF/kotlinx-serialization-cbor.kotlin_module'
|
||||||
|
exclude 'META-INF/kotlinx-serialization-properties.kotlin_module'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,8 @@ allprojects {
|
|||||||
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject"
|
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject"
|
||||||
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject"
|
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject"
|
||||||
|
|
||||||
|
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings"
|
||||||
|
|
||||||
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
|
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
|
||||||
publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle"
|
publishMavenPath = "${rootProject.projectDir.absolutePath}/maven.publish.gradle"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ org.gradle.parallel=true
|
|||||||
kotlin.js.generate.externals=true
|
kotlin.js.generate.externals=true
|
||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
|
||||||
kotlin_version=1.4.10
|
kotlin_version=1.4.10
|
||||||
kotlin_coroutines_version=1.4.1
|
kotlin_coroutines_version=1.4.1
|
||||||
@ -20,10 +22,15 @@ uuidVersion=0.2.2
|
|||||||
|
|
||||||
# ANDROID
|
# ANDROID
|
||||||
|
|
||||||
|
core_ktx_version=1.3.2
|
||||||
|
|
||||||
android_minSdkVersion=24
|
android_minSdkVersion=24
|
||||||
android_compileSdkVersion=30
|
android_compileSdkVersion=30
|
||||||
android_buildToolsVersion=30.0.2
|
android_buildToolsVersion=30.0.2
|
||||||
dexcount_version=2.0.0-RC1
|
dexcount_version=2.0.0-RC1
|
||||||
|
junit_version=4.12
|
||||||
|
test_ext_junit_version=1.1.2
|
||||||
|
espresso_core=3.3.0
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.3.1
|
version=0.3.1
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
ktor/client/src/main/AndroidManifest.xml
Normal file
1
ktor/client/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.ktor.client"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
ktor/common/src/main/AndroidManifest.xml
Normal file
1
ktor/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.ktor.common"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
mime_types/src/main/AndroidManifest.xml
Normal file
1
mime_types/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.mime_types"/>
|
@ -21,32 +21,4 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
apply from: "$defaultAndroidSettingsPresetPath"
|
||||||
compileSdkVersion "$android_compileSdkVersion".toInteger()
|
|
||||||
defaultConfig {
|
|
||||||
minSdkVersion "$android_minSdkVersion".toInteger()
|
|
||||||
targetSdkVersion "$android_compileSdkVersion".toInteger()
|
|
||||||
versionCode "${android_code_version}".toInteger()
|
|
||||||
versionName "$version"
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packagingOptions {
|
|
||||||
exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module'
|
|
||||||
exclude 'META-INF/kotlinx-serialization-cbor.kotlin_module'
|
|
||||||
exclude 'META-INF/kotlinx-serialization-properties.kotlin_module'
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -9,6 +9,9 @@ kotlin {
|
|||||||
browser()
|
browser()
|
||||||
nodejs()
|
nodejs()
|
||||||
}
|
}
|
||||||
|
android {
|
||||||
|
publishLibraryVariants()
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
@ -34,5 +37,14 @@ kotlin {
|
|||||||
implementation kotlin('test-junit')
|
implementation kotlin('test-junit')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
androidTest {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('test-junit')
|
||||||
|
implementation "androidx.test.ext:junit:$test_ext_junit_version"
|
||||||
|
implementation "androidx.test.espresso:espresso-core:$espresso_core"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply from: "$defaultAndroidSettingsPresetPath"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
pagination/common/src/main/AndroidManifest.xml
Normal file
1
pagination/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.pagination.common"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
pagination/ktor/common/src/main/AndroidManifest.xml
Normal file
1
pagination/ktor/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.pagination.ktor.common"/>
|
@ -6,3 +6,19 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppAndroidProjectPresetPath"
|
apply from: "$mppAndroidProjectPresetPath"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api internalProject("micro_utils.repos.common")
|
||||||
|
api internalProject("micro_utils.coroutines")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
implementation "androidx.core:core-ktx:$core_ktx_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
<manifest package="dev.inmo.repos.android"/>
|
<manifest package="${manifestPackage}"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
@ -15,5 +17,13 @@ kotlin {
|
|||||||
api "com.benasher44:uuid:$uuidVersion"
|
api "com.benasher44:uuid:$uuidVersion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidMain {
|
||||||
|
dependencies {
|
||||||
|
api "androidx.core:core-ktx:$core_ktx_version"
|
||||||
|
api internalProject("micro_utils.common")
|
||||||
|
api internalProject("micro_utils.coroutines")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
1
repos/common/src/main/AndroidManifest.xml
Normal file
1
repos/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.repos.common"/>
|
@ -0,0 +1,46 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
sealed class ColumnType(
|
||||||
|
typeName: String,
|
||||||
|
nullable: Boolean
|
||||||
|
) {
|
||||||
|
open val asType: String = "$typeName${if (!nullable) " not null" else ""}"
|
||||||
|
sealed class Text(
|
||||||
|
nullable: Boolean
|
||||||
|
) : ColumnType("text", nullable) {
|
||||||
|
object NULLABLE : Text(true)
|
||||||
|
object NOT_NULLABLE : Text(false)
|
||||||
|
}
|
||||||
|
sealed class Numeric(
|
||||||
|
typeName: String,
|
||||||
|
autoincrement: Boolean = false,
|
||||||
|
primaryKey: Boolean = false,
|
||||||
|
nullable: Boolean = false
|
||||||
|
) : ColumnType(
|
||||||
|
typeName,
|
||||||
|
nullable
|
||||||
|
) {
|
||||||
|
override val asType: String = "${super.asType}${if (primaryKey) " primary key" else ""}${if (autoincrement) " autoincrement" else ""}"
|
||||||
|
|
||||||
|
class INTEGER(
|
||||||
|
autoincrement: Boolean = false,
|
||||||
|
primaryKey: Boolean = false,
|
||||||
|
nullable: Boolean = false
|
||||||
|
) : Numeric(
|
||||||
|
"integer",
|
||||||
|
autoincrement,
|
||||||
|
primaryKey,
|
||||||
|
nullable
|
||||||
|
)
|
||||||
|
class DOUBLE(autoincrement: Boolean = false, primaryKey: Boolean = false, nullable: Boolean = false) : Numeric(
|
||||||
|
"double",
|
||||||
|
autoincrement,
|
||||||
|
primaryKey,
|
||||||
|
nullable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return asType
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST", "SimplifiableCall")
|
||||||
|
fun contentValuesOfNotNull(vararg pairs: Pair<String, Any?>?) = contentValuesOf(
|
||||||
|
*(pairs.filter { it != null } as List<Pair<String, Any?>>).toTypedArray()
|
||||||
|
)
|
@ -0,0 +1,21 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import android.database.Cursor
|
||||||
|
|
||||||
|
inline fun <T> Cursor.map(
|
||||||
|
block: (Cursor) -> T
|
||||||
|
): List<T> {
|
||||||
|
val result = mutableListOf<T>()
|
||||||
|
if (moveToFirst()) {
|
||||||
|
do {
|
||||||
|
result.add(block(this))
|
||||||
|
} while (moveToNext())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Cursor.firstOrNull(): Cursor? = if (moveToFirst()) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import kotlinx.coroutines.newSingleThreadContext
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
val DatabaseCoroutineContext: CoroutineContext = newSingleThreadContext("db-context")
|
@ -0,0 +1,64 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
|
||||||
|
fun createTableQuery(
|
||||||
|
tableName: String,
|
||||||
|
vararg columnsToTypes: Pair<String, ColumnType>
|
||||||
|
) = "create table $tableName (${columnsToTypes.joinToString(", ") { "${it.first} ${it.second}" }});"
|
||||||
|
|
||||||
|
fun SQLiteDatabase.createTable(
|
||||||
|
tableName: String,
|
||||||
|
vararg columnsToTypes: Pair<String, ColumnType>,
|
||||||
|
onInit: (SQLiteDatabase.() -> Unit)? = null
|
||||||
|
): Boolean {
|
||||||
|
val existing = rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='$tableName'", null).use {
|
||||||
|
it.count > 0
|
||||||
|
}
|
||||||
|
return if (existing) {
|
||||||
|
false
|
||||||
|
// TODO:: add upgrade opportunity
|
||||||
|
} else {
|
||||||
|
execSQL(createTableQuery(tableName, *columnsToTypes))
|
||||||
|
onInit ?.invoke(this)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Cursor.getString(columnName: String) = getString(
|
||||||
|
getColumnIndex(columnName)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Cursor.getLong(columnName: String) = getLong(
|
||||||
|
getColumnIndex(columnName)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Cursor.getInt(columnName: String) = getInt(
|
||||||
|
getColumnIndex(columnName)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Cursor.getDouble(columnName: String) = getDouble(
|
||||||
|
getColumnIndex(columnName)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun SQLiteDatabase.select(
|
||||||
|
table: String,
|
||||||
|
columns: Array<String>? = null,
|
||||||
|
selection: String? = null,
|
||||||
|
selectionArgs: Array<String>? = null,
|
||||||
|
groupBy: String? = null,
|
||||||
|
having: String? = null,
|
||||||
|
orderBy: String? = null,
|
||||||
|
limit: String? = null
|
||||||
|
) = query(
|
||||||
|
table, columns, selection, selectionArgs, groupBy, having, orderBy, limit
|
||||||
|
)
|
||||||
|
|
||||||
|
fun makePlaceholders(count: Int): String {
|
||||||
|
return (0 until count).joinToString { "?" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun makeStringPlaceholders(count: Int): String {
|
||||||
|
return (0 until count).joinToString { "\"?\"" }
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.firstIndex
|
||||||
|
|
||||||
|
fun limitClause(size: Long, since: Long? = null) = "${since ?.let { "$it, " } ?: ""}$size"
|
||||||
|
fun limitClause(size: Int, since: Int? = null) = limitClause(size.toLong(), since ?.toLong())
|
||||||
|
fun Pagination.limitClause() = limitClause(size, firstIndex)
|
@ -0,0 +1,45 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
suspend fun <T> SQLiteDatabase.transaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
|
return withContext(DatabaseCoroutineContext) {
|
||||||
|
when {
|
||||||
|
inTransaction() -> {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
beginTransaction()
|
||||||
|
safely(
|
||||||
|
{
|
||||||
|
endTransaction()
|
||||||
|
throw it
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
block().also {
|
||||||
|
setTransactionSuccessful()
|
||||||
|
endTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> SQLiteDatabase.inlineTransaction(block: SQLiteDatabase.() -> T): T {
|
||||||
|
return when {
|
||||||
|
inTransaction() -> block()
|
||||||
|
else -> {
|
||||||
|
beginTransaction()
|
||||||
|
try {
|
||||||
|
block().also {
|
||||||
|
setTransactionSuccessful()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
endTransaction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
val internalId = "_id"
|
||||||
|
val internalIdType = ColumnType.Numeric.INTEGER(
|
||||||
|
autoincrement = true,
|
||||||
|
primaryKey = true
|
||||||
|
)
|
@ -0,0 +1,51 @@
|
|||||||
|
package dev.inmo.micro_utils.repos
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.DatabaseErrorHandler
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import dev.inmo.micro_utils.coroutines.safely
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
|
private data class CallbackContinuationPair<T> (
|
||||||
|
val callback: suspend SQLiteDatabase.() -> T,
|
||||||
|
val continuation: Continuation<T>
|
||||||
|
) {
|
||||||
|
suspend fun SQLiteDatabase.execute() {
|
||||||
|
safely(
|
||||||
|
{
|
||||||
|
continuation.resumeWithException(it)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
continuation.resume(callback())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StandardSQLHelper(
|
||||||
|
context: Context,
|
||||||
|
name: String,
|
||||||
|
factory: SQLiteDatabase.CursorFactory? = null,
|
||||||
|
version: Int = 1,
|
||||||
|
errorHandler: DatabaseErrorHandler? = null
|
||||||
|
) {
|
||||||
|
val sqlOpenHelper = object : SQLiteOpenHelper(context, name, factory, version, errorHandler) {
|
||||||
|
override fun onCreate(db: SQLiteDatabase?) {}
|
||||||
|
|
||||||
|
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)
|
||||||
|
|
||||||
|
suspend fun <T> readableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.readableTransaction(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> SQLiteOpenHelper.writableTransaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
|
return writableDatabase.transaction(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> SQLiteOpenHelper.readableTransaction(block: suspend SQLiteDatabase.() -> T): T {
|
||||||
|
return readableDatabase.transaction(block)
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.crud
|
||||||
|
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.createPaginationResult
|
||||||
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
|
||||||
|
val <T> T.asId: String
|
||||||
|
get() = (this as? String) ?: this!!.toString()
|
||||||
|
|
||||||
|
abstract class AbstractAndroidCRUDRepo<ObjectType, IdType>(
|
||||||
|
protected val helper: StandardSQLHelper
|
||||||
|
) : ReadStandardCRUDRepo<ObjectType, IdType> {
|
||||||
|
protected abstract val tableName: String
|
||||||
|
protected abstract val idColumnName: String
|
||||||
|
protected abstract suspend fun Cursor.toObject(): ObjectType
|
||||||
|
protected fun SQLiteDatabase.count(): Long = select(tableName).use {
|
||||||
|
it.count
|
||||||
|
}.toLong()
|
||||||
|
|
||||||
|
override suspend fun contains(id: IdType): Boolean = helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
null,
|
||||||
|
"$idColumnName=?",
|
||||||
|
arrayOf(id.asId)
|
||||||
|
).use {
|
||||||
|
it.count > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getById(id: IdType): ObjectType? = helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
selection = "$idColumnName=?",
|
||||||
|
selectionArgs = arrayOf(id.asId),
|
||||||
|
limit = limitClause(1)
|
||||||
|
).use { c ->
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
c.toObject()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
|
||||||
|
return helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
limit = pagination.limitClause()
|
||||||
|
).use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
val resultList = mutableListOf(it.toObject())
|
||||||
|
while (it.moveToNext()) {
|
||||||
|
resultList.add(it.toObject())
|
||||||
|
}
|
||||||
|
resultList.createPaginationResult(pagination, count())
|
||||||
|
} else {
|
||||||
|
emptyList<ObjectType>().createPaginationResult(pagination, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.crud
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import dev.inmo.micro_utils.common.mapNotNullA
|
||||||
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
import kotlinx.coroutines.channels.BroadcastChannel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
|
||||||
|
abstract class AbstractMutableAndroidCRUDRepo<ObjectType, IdType, InputValueType>(
|
||||||
|
helper: StandardSQLHelper
|
||||||
|
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>,
|
||||||
|
AbstractAndroidCRUDRepo<ObjectType, IdType>(helper) {
|
||||||
|
protected val newObjectsChannel = BroadcastChannel<ObjectType>(64)
|
||||||
|
protected val updateObjectsChannel = BroadcastChannel<ObjectType>(64)
|
||||||
|
protected val deleteObjectsIdsChannel = BroadcastChannel<IdType>(64)
|
||||||
|
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asFlow()
|
||||||
|
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asFlow()
|
||||||
|
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
|
||||||
|
|
||||||
|
protected abstract suspend fun InputValueType.asContentValues(id: IdType? = null): ContentValues
|
||||||
|
|
||||||
|
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
|
||||||
|
val indexes = helper.writableTransaction {
|
||||||
|
values.map {
|
||||||
|
insert(tableName, null, it.asContentValues())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return helper.readableTransaction {
|
||||||
|
indexes.mapNotNullA { i ->
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
selection = "$internalId=?",
|
||||||
|
selectionArgs = arrayOf(i.toString())
|
||||||
|
).use { c ->
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
c.toObject()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
it.forEach {
|
||||||
|
newObjectsChannel.send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteById(ids: List<IdType>) {
|
||||||
|
val deleted = mutableListOf<IdType>()
|
||||||
|
helper.writableTransaction {
|
||||||
|
ids.forEach { id ->
|
||||||
|
delete(tableName, "$idColumnName=?", arrayOf(id.asId)).also {
|
||||||
|
if (it > 0) {
|
||||||
|
deleted.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleted.forEach {
|
||||||
|
deleteObjectsIdsChannel.send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
|
||||||
|
val asContentValues = value.asContentValues(id)
|
||||||
|
if (asContentValues.keySet().isNotEmpty()) {
|
||||||
|
helper.writableTransaction {
|
||||||
|
update(
|
||||||
|
tableName,
|
||||||
|
asContentValues,
|
||||||
|
"$idColumnName=?",
|
||||||
|
arrayOf(id.asId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getById(id) ?.also {
|
||||||
|
updateObjectsChannel.send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
||||||
|
helper.writableTransaction {
|
||||||
|
values.forEach { (id, value) ->
|
||||||
|
update(
|
||||||
|
tableName,
|
||||||
|
value.asContentValues(id),
|
||||||
|
"$idColumnName=?",
|
||||||
|
arrayOf(id.asId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values.mapNotNullA {
|
||||||
|
getById(it.first)
|
||||||
|
}.also {
|
||||||
|
it.forEach {
|
||||||
|
updateObjectsChannel.send(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long = helper.readableTransaction { select(tableName).use { it.count.toLong() } }
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.keyvalue
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.paginate
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.reverse
|
||||||
|
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
|
||||||
|
import kotlinx.coroutines.channels.BroadcastChannel
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
|
||||||
|
private val cache = HashMap<String, KeyValueStore<*>>()
|
||||||
|
|
||||||
|
fun <T : Any> Context.keyValueStore(
|
||||||
|
name: String = "default",
|
||||||
|
cacheValues: Boolean = false
|
||||||
|
): StandardKeyValueRepo<String, T> {
|
||||||
|
return cache.getOrPut(name) {
|
||||||
|
KeyValueStore<T>(this, name, cacheValues)
|
||||||
|
} as KeyValueStore<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyValueStore<T : Any> internal constructor (
|
||||||
|
c: Context,
|
||||||
|
preferencesName: String,
|
||||||
|
useCache: Boolean = false
|
||||||
|
) : SharedPreferences.OnSharedPreferenceChangeListener, StandardKeyValueRepo<String, T> {
|
||||||
|
private val sharedPreferences = c.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
private val cachedData = if (useCache) {
|
||||||
|
mutableMapOf<String, Any>()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val onNewValueChannel = BroadcastChannel<Pair<String, T>>(Channel.BUFFERED)
|
||||||
|
private val onValueRemovedChannel = BroadcastChannel<String>(Channel.BUFFERED)
|
||||||
|
|
||||||
|
override val onNewValue: Flow<Pair<String, T>> = onNewValueChannel.asFlow()
|
||||||
|
override val onValueRemoved: Flow<String> = onValueRemovedChannel.asFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
cachedData ?.let {
|
||||||
|
sharedPreferences.all.forEach {
|
||||||
|
if (it.value != null) {
|
||||||
|
cachedData[it.key] = it.value as Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sp: SharedPreferences, key: String) {
|
||||||
|
val value = sp.all[key]
|
||||||
|
cachedData ?: return
|
||||||
|
if (value != null) {
|
||||||
|
cachedData[key] = value
|
||||||
|
} else {
|
||||||
|
cachedData.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun get(k: String): T? {
|
||||||
|
return (cachedData ?. get(k) ?: sharedPreferences.all[k]) as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<T> {
|
||||||
|
val resultPagination = if (reversed) pagination.reverse(count()) else pagination
|
||||||
|
return sharedPreferences.all.values.paginate(
|
||||||
|
resultPagination
|
||||||
|
).let {
|
||||||
|
PaginationResult(
|
||||||
|
it.page,
|
||||||
|
it.pagesNumber,
|
||||||
|
it.results.map { it as T }.let { if (reversed) it.reversed() else it },
|
||||||
|
it.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<String> {
|
||||||
|
val resultPagination = if (reversed) pagination.reverse(count()) else pagination
|
||||||
|
return sharedPreferences.all.keys.paginate(
|
||||||
|
resultPagination
|
||||||
|
).let {
|
||||||
|
PaginationResult(
|
||||||
|
it.page,
|
||||||
|
it.pagesNumber,
|
||||||
|
it.results.let { if (reversed) it.reversed() else it },
|
||||||
|
it.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(key: String): Boolean = sharedPreferences.contains(key)
|
||||||
|
|
||||||
|
override suspend fun count(): Long = sharedPreferences.all.size.toLong()
|
||||||
|
|
||||||
|
override suspend fun set(toSet: Map<String, T>) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
toSet.forEach { (k, v) ->
|
||||||
|
when(v) {
|
||||||
|
is Int -> putInt(k, v)
|
||||||
|
is Long -> putLong(k, v)
|
||||||
|
is Float -> putFloat(k, v)
|
||||||
|
is String -> putString(k, v)
|
||||||
|
is Boolean -> putBoolean(k, v)
|
||||||
|
is Set<*> -> putStringSet(k, v.map { (it as? String) ?: it.toString() }.toSet())
|
||||||
|
else -> error(
|
||||||
|
"Currently supported only primitive types and set for SharedPreferences KeyValue repos"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toSet.forEach { (k, v) ->
|
||||||
|
onNewValueChannel.send(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unset(toUnset: List<String>) {
|
||||||
|
sharedPreferences.edit {
|
||||||
|
toUnset.forEach { remove(it) }
|
||||||
|
}
|
||||||
|
toUnset.forEach { onValueRemovedChannel.send(it) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.onetomany
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import dev.inmo.micro_utils.common.mapNotNullA
|
||||||
|
import dev.inmo.micro_utils.pagination.FirstPagePagination
|
||||||
|
import dev.inmo.micro_utils.pagination.Pagination
|
||||||
|
import dev.inmo.micro_utils.pagination.PaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.createPaginationResult
|
||||||
|
import dev.inmo.micro_utils.pagination.utils.reverse
|
||||||
|
import dev.inmo.micro_utils.repos.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
private val internalSerialFormat = Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
}
|
||||||
|
|
||||||
|
class OneToManyAndroidRepo<Key, Value>(
|
||||||
|
private val tableName: String,
|
||||||
|
private val keySerializer: KSerializer<Key>,
|
||||||
|
private val valueSerializer: KSerializer<Value>,
|
||||||
|
private val helper: SQLiteOpenHelper
|
||||||
|
) : OneToManyKeyValueRepo<Key, Value> {
|
||||||
|
private val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
|
||||||
|
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
|
||||||
|
private val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
|
||||||
|
override val onValueRemoved: Flow<Pair<Key, Value>> = _onValueRemoved.asSharedFlow()
|
||||||
|
private val _onDataCleared = MutableSharedFlow<Key>()
|
||||||
|
override val onDataCleared: Flow<Key> = _onDataCleared.asSharedFlow()
|
||||||
|
|
||||||
|
private val idColumnName = "id"
|
||||||
|
private val valueColumnName = "value"
|
||||||
|
|
||||||
|
private fun Key.asId() = internalSerialFormat.encodeToString(keySerializer, this)
|
||||||
|
private fun Value.asValue() = internalSerialFormat.encodeToString(valueSerializer, this)
|
||||||
|
private fun String.asValue(): Value = internalSerialFormat.decodeFromString(valueSerializer, this)
|
||||||
|
private fun String.asKey(): Key = internalSerialFormat.decodeFromString(keySerializer, this)
|
||||||
|
|
||||||
|
init {
|
||||||
|
runBlocking(DatabaseCoroutineContext) {
|
||||||
|
helper.writableTransaction {
|
||||||
|
createTable(
|
||||||
|
tableName,
|
||||||
|
internalId to internalIdType,
|
||||||
|
idColumnName to ColumnType.Text.NOT_NULLABLE,
|
||||||
|
valueColumnName to ColumnType.Text.NULLABLE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||||
|
val added = mutableListOf<Pair<Key, Value>>()
|
||||||
|
helper.writableTransaction {
|
||||||
|
toAdd.forEach { (k, values) ->
|
||||||
|
values.forEach { v ->
|
||||||
|
insert(
|
||||||
|
tableName,
|
||||||
|
null,
|
||||||
|
contentValuesOf(
|
||||||
|
idColumnName to k.asId(),
|
||||||
|
valueColumnName to v.asValue()
|
||||||
|
)
|
||||||
|
).also {
|
||||||
|
if (it != -1L) {
|
||||||
|
added.add(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
added.forEach { _onNewValue.emit(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear(k: Key) {
|
||||||
|
helper.writableTransaction {
|
||||||
|
delete(tableName, "$idColumnName=?", arrayOf(k.asId()))
|
||||||
|
}.also {
|
||||||
|
if (it > 0) {
|
||||||
|
_onDataCleared.emit(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key): Boolean = helper.readableTransaction {
|
||||||
|
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
|
||||||
|
it.count > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key, v: Value): Boolean = helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
selection = "$idColumnName=? AND $valueColumnName=?",
|
||||||
|
selectionArgs = arrayOf(k.asId(), v.asValue()),
|
||||||
|
limit = FirstPagePagination(1).limitClause()
|
||||||
|
).use {
|
||||||
|
it.count > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long =helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName
|
||||||
|
).use {
|
||||||
|
it.count
|
||||||
|
}
|
||||||
|
}.toLong()
|
||||||
|
|
||||||
|
override suspend fun count(k: Key): Long = helper.readableTransaction {
|
||||||
|
select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.asId()), limit = FirstPagePagination(1).limitClause()).use {
|
||||||
|
it.count
|
||||||
|
}
|
||||||
|
}.toLong()
|
||||||
|
|
||||||
|
override suspend fun get(
|
||||||
|
k: Key,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Value> = count(k).let { count ->
|
||||||
|
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
||||||
|
helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
selection = "$idColumnName=?",
|
||||||
|
selectionArgs = arrayOf(k.asId()),
|
||||||
|
limit = resultPagination.limitClause()
|
||||||
|
).use { c ->
|
||||||
|
mutableListOf<Value>().also {
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
it.add(c.getString(valueColumnName).asValue())
|
||||||
|
} while (c.moveToNext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.createPaginationResult(
|
||||||
|
pagination,
|
||||||
|
count
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Key> = count().let { count ->
|
||||||
|
val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination }
|
||||||
|
helper.readableTransaction {
|
||||||
|
select(
|
||||||
|
tableName,
|
||||||
|
limit = resultPagination.limitClause()
|
||||||
|
).use { c ->
|
||||||
|
mutableListOf<Key>().also {
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
it.add(c.getString(idColumnName).asKey())
|
||||||
|
} while (c.moveToNext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.createPaginationResult(
|
||||||
|
pagination,
|
||||||
|
count
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
|
helper.writableTransaction {
|
||||||
|
toRemove.flatMap { (k, vs) ->
|
||||||
|
vs.mapNotNullA { v ->
|
||||||
|
if (delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(k.asId(), v.asValue())) > 0) {
|
||||||
|
k to v
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.forEach { (k, v) ->
|
||||||
|
_onValueRemoved.emit(k to v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
repos/inmemory/src/main/AndroidManifest.xml
Normal file
1
repos/inmemory/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.repos.inmemory"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
repos/ktor/client/src/main/AndroidManifest.xml
Normal file
1
repos/ktor/client/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.repos.ktor.client"/>
|
@ -1,6 +1,8 @@
|
|||||||
plugins {
|
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 "kotlin-android-extensions"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$mppProjectWithSerializationPresetPath"
|
apply from: "$mppProjectWithSerializationPresetPath"
|
||||||
|
1
repos/ktor/common/src/main/AndroidManifest.xml
Normal file
1
repos/ktor/common/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1 @@
|
|||||||
|
<manifest package="dev.inmo.micro_utils.repos.ktor.common"/>
|
@ -13,7 +13,6 @@ String[] includes = [
|
|||||||
":repos:ktor:client",
|
":repos:ktor:client",
|
||||||
":repos:ktor:common",
|
":repos:ktor:common",
|
||||||
":repos:ktor:server",
|
":repos:ktor:server",
|
||||||
":repos:android",
|
|
||||||
":ktor:server",
|
":ktor:server",
|
||||||
":ktor:common",
|
":ktor:common",
|
||||||
":ktor:client",
|
":ktor:client",
|
||||||
|
Loading…
Reference in New Issue
Block a user