diff --git a/CHANGELOG.md b/CHANGELOG.md index 778de2ec70a..caa403afbaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.12.14 + +* `Versions`: + * `Android CoreKTX`: `1.8.0` -> `1.9.0` + * `Android AppCompat`: `1.4.2` -> `1.5.1` + * Android Compile SDK: 32 -> 33 + * Android Build Tools: 32.0.0 -> 33.0.0 +* `Common`: + * `Android`: + * Add `argumentOrNull`/`argumentOrThrow` delegates for fragments +* `Coroutines`: + * Rewrite `awaitFirstWithDeferred` onto `CompletableDeferred` instead of coroutines suspending + ## 0.12.13 * `Coroutines`: diff --git a/common/build.gradle b/common/build.gradle index 14324f2f227..880abb6a3a2 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -16,6 +16,7 @@ kotlin { androidMain { dependencies { api project(":micro_utils.coroutines") + api libs.android.fragment } } } diff --git a/common/src/main/kotlin/dev/inmo/micro_utils/common/ArgumentPropertyNullableDelegate.kt b/common/src/main/kotlin/dev/inmo/micro_utils/common/ArgumentPropertyNullableDelegate.kt new file mode 100644 index 00000000000..07d4abeab2a --- /dev/null +++ b/common/src/main/kotlin/dev/inmo/micro_utils/common/ArgumentPropertyNullableDelegate.kt @@ -0,0 +1,76 @@ +package dev.inmo.micro_utils.common + +import android.os.Bundle +import android.os.Parcelable +import androidx.fragment.app.Fragment +import java.io.Serializable +import kotlin.reflect.KProperty + +object ArgumentPropertyNullableDelegate { + operator fun getValue(thisRef: Fragment, property: KProperty<*>): T? { + val arguments = thisRef.arguments ?: return null + val key = property.name + return when (property.getter.returnType.classifier) { + // Scalars + String::class -> arguments.getString(key) + Boolean::class -> arguments.getBoolean(key) + Byte::class -> arguments.getByte(key) + Char::class -> arguments.getChar(key) + Double::class -> arguments.getDouble(key) + Float::class -> arguments.getFloat(key) + Int::class -> arguments.getInt(key) + Long::class -> arguments.getLong(key) + Short::class -> arguments.getShort(key) + + // References + Bundle::class -> arguments.getBundle(key) + CharSequence::class -> arguments.getCharSequence(key) + Parcelable::class -> arguments.getParcelable(key) + + // Scalar arrays + BooleanArray::class -> arguments.getBooleanArray(key) + ByteArray::class -> arguments.getByteArray(key) + CharArray::class -> arguments.getCharArray(key) + DoubleArray::class -> arguments.getDoubleArray(key) + FloatArray::class -> arguments.getFloatArray(key) + IntArray::class -> arguments.getIntArray(key) + LongArray::class -> arguments.getLongArray(key) + ShortArray::class -> arguments.getShortArray(key) + Array::class -> { + val componentType = property.returnType.classifier ?.javaClass ?.componentType!! + @Suppress("UNCHECKED_CAST") // Checked by reflection. + when { + Parcelable::class.java.isAssignableFrom(componentType) -> { + arguments.getParcelableArray(key) + } + String::class.java.isAssignableFrom(componentType) -> { + arguments.getStringArray(key) + } + CharSequence::class.java.isAssignableFrom(componentType) -> { + arguments.getCharSequenceArray(key) + } + Serializable::class.java.isAssignableFrom(componentType) -> { + arguments.getSerializable(key) + } + else -> { + val valueType = componentType.canonicalName + throw IllegalArgumentException( + "Illegal value array type $valueType for key \"$key\"" + ) + } + } + } + Serializable::class -> arguments.getSerializable(key) + else -> null + } as? T + } +} + +object ArgumentPropertyNonNullableDelegate { + operator fun getValue(thisRef: Fragment, property: KProperty<*>): T { + return ArgumentPropertyNullableDelegate.getValue(thisRef, property)!! + } +} + +fun argumentOrNull() = ArgumentPropertyNullableDelegate +fun argumentOrThrow() = ArgumentPropertyNonNullableDelegate diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/AwaitFirst.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/AwaitFirst.kt index 4c096027fd9..6f85dac8004 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/AwaitFirst.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/AwaitFirst.kt @@ -6,23 +6,19 @@ import kotlin.coroutines.* suspend fun Iterable>.awaitFirstWithDeferred( scope: CoroutineScope, cancelOnResult: Boolean = true -): Pair, T> = suspendCoroutine, T>> { continuation -> - scope.launch(SupervisorJob()) { - val scope = this - forEach { - scope.launch { - continuation.resume(it to it.await()) - scope.cancel() - } +): Pair, T> { + val resultDeferred = CompletableDeferred, T>>() + val scope = scope.LinkedSupervisorScope() + forEach { + scope.launch { + resultDeferred.complete(it to it.await()) + scope.cancel() } } -}.also { - if (cancelOnResult) { - forEach { - try { - it.cancel() - } catch (e: IllegalStateException) { - e.printStackTrace() + return resultDeferred.await().also { + if (cancelOnResult) { + forEach { + runCatchingSafely { it.cancel() } } } } diff --git a/gradle.properties b/gradle.properties index 0ace4b26bc3..5c3e84ae4f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.12.13 -android_code_version=152 +version=0.12.14 +android_code_version=153 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13617be09b3..a8b63739d6e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,15 +20,16 @@ koin = "3.2.0" android-gradle = "7.2.2" dexcount = "3.1.0" -android-coreKtx = "1.8.0" +android-coreKtx = "1.9.0" android-recyclerView = "1.2.1" -android-appCompat = "1.4.2" +android-appCompat = "1.5.1" +android-fragment = "1.5.3" android-espresso = "3.4.0" android-test = "1.1.3" android-props-minSdk = "21" -android-props-compileSdk = "32" -android-props-buildTools = "32.0.0" +android-props-compileSdk = "33" +android-props-buildTools = "33.0.0" [libraries] @@ -71,6 +72,7 @@ jb-exposed = { module = "org.jetbrains.exposed:exposed-core", version.ref = "jb- android-coreKtx = { module = "androidx.core:core-ktx", version.ref = "android-coreKtx" } android-recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "android-recyclerView" } android-appCompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "android-appCompat" } +android-fragment = { module = "androidx.fragment:fragment", version.ref = "android-fragment" } android-espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "android-espresso" } android-test-junit = { module = "androidx.test.ext:junit", version.ref = "android-test" }