Update Kotlin, Serialization and Coroutines
* Kotlin [1.3.72] -> [1.4.10] * Serialization [0.20.0] -> [1.0.0] * Coroutines [1.3.4] -> [1.4.0-M1] CP-994
This commit is contained in:
parent
c5ee8ecb66
commit
4e0d9d3dd5
|
@ -30,20 +30,19 @@ plugins {
|
|||
id("me.proton.kotlin")
|
||||
id("me.proton.publish-libraries")
|
||||
id("me.proton.tests")
|
||||
|
||||
val kotlinVersion = "1.4.10" // Sep 09, 2020
|
||||
|
||||
kotlin("jvm") version kotlinVersion apply false
|
||||
kotlin("plugin.serialization") version kotlinVersion apply false
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories.google()
|
||||
|
||||
dependencies {
|
||||
val kotlinVersion = "1.4.10" // Sep 09, 2020
|
||||
val agpVersion = "4.2.0-alpha13"
|
||||
val hiltVersion = "2.29.1-alpha" // Sep 10, 2020
|
||||
|
||||
classpath(kotlin("gradle-plugin", kotlinVersion))
|
||||
classpath(kotlin("serialization", kotlinVersion))
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:$kotlinVersion")
|
||||
classpath("com.android.tools.build:gradle:$agpVersion")
|
||||
classpath("com.google.dagger:hilt-android-gradle-plugin:$hiltVersion")
|
||||
}
|
||||
|
@ -55,7 +54,8 @@ kotlinCompilerArgs(
|
|||
// Enables inline classes
|
||||
"-XXLanguage:+InlineClasses",
|
||||
// Enables experimental Coroutines from coroutines-test artifact, like `runBlockingTest`
|
||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
|
||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi"
|
||||
)
|
||||
// setupDokka()
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
val easyGradle = "2.6" // Oct 15, 2020
|
||||
val easyGradle = "2.7" // Oct 15, 2020
|
||||
val agpVersion = "4.2.0-alpha13" // Oct 01, 2020
|
||||
|
||||
implementation(gradleApi())
|
||||
|
|
|
@ -22,14 +22,12 @@ import studio.forface.easygradle.dsl.android.*
|
|||
internal fun initVersions() {
|
||||
|
||||
// region Kotlin
|
||||
`kotlin version` = "1.3.72" // Released: Apr 14, 2020
|
||||
`coroutines version` = "1.3.5" // Released: Mar 17, 2020
|
||||
`serialization version` = "0.20.0" // Released: Mar 04, 2020
|
||||
`kotlin version` = "1.4.10" // Released: Sep 09, 2020
|
||||
`coroutines version` = "1.4.0-M1" // Released: Oct 13, 2020
|
||||
`serialization version` = "1.0.0" // Released: Oct 08, 2020
|
||||
// endregion
|
||||
|
||||
// region Android
|
||||
`android-gradle-plugin version` = "4.0.0-beta03" // Released: Mar 19, 2020
|
||||
|
||||
`activity version` = "1.2.0-alpha03" // Released: Apr 05, 2020
|
||||
`android-annotation version` = "1.1.0" // Released: Jun 05, 2019
|
||||
`appcompat version` = "1.1.0" // Released: Sep 06, 2019
|
||||
|
@ -44,7 +42,7 @@ internal fun initVersions() {
|
|||
`android-work version` = "2.2.0" // Released: Aug 16, 2019
|
||||
|
||||
`android-test version` = "1.2.0" // Released: May 31, 2019
|
||||
`robolectric version` = "4.3.1" // Released: Oct 11, 2019
|
||||
`robolectric version` = "4.4" // Released: Aug 24, 2020
|
||||
// endregion
|
||||
|
||||
// region Others
|
||||
|
@ -54,15 +52,12 @@ internal fun initVersions() {
|
|||
`hilt-androidx version` = "1.0.0-alpha01" // Released: Jun 12, 2020
|
||||
`mockK version` = "1.10.0" // Released: Apr 19, 2020
|
||||
`retrofit version` = "2.9.0" // Released: May 20, 2020
|
||||
`retrofit-kotlin-serialization version` = "0.4.0" // Released: Apr 12, 2019
|
||||
`retrofit-kotlin-serialization version` = "0.8.0" // Released: Oct 09, 2020
|
||||
`timber version` = "4.7.1" // Released:
|
||||
`viewStateStore version` = "1.4-beta-4" // Released: Oct 03, 2019
|
||||
// endregion
|
||||
}
|
||||
|
||||
// region Kotlin
|
||||
// endregion
|
||||
|
||||
// region Android
|
||||
const val `android-tools version` = "26.6.3" // Updated: Apr 17, 2020
|
||||
const val `androidUi version` = "0.1.0-dev08" // Released: Apr 03, 2020
|
||||
|
@ -74,11 +69,3 @@ const val `miniDsn version` = "1.0.0" // Released: Jul 18,
|
|||
const val `okHttp version` = "4.8.0" // Released: Jul 11, 2020
|
||||
const val `trustKit version` = "1.1.3" // Released: Apr 30, 2020
|
||||
// endregion
|
||||
|
||||
// region Gradle
|
||||
const val `easyGradle version` = "1.5-beta-10" // Released: Jun 27, 2020
|
||||
// endregion
|
||||
|
||||
// region Plugins
|
||||
const val `dokka-plugin version` = "0.10.1" // Released: Feb 04, 2020
|
||||
// endregion
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
*/
|
||||
package me.proton.core.network.data.protonApi
|
||||
|
||||
import kotlinx.serialization.Decoder
|
||||
import kotlinx.serialization.Encoder
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialDescriptor
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.JsonInput
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
/**
|
||||
* Kotlin Boolean serializer that can deserialize Boolean from both bool and int json values
|
||||
|
@ -31,20 +30,17 @@ import kotlinx.serialization.json.JsonInput
|
|||
*/
|
||||
class IntToBoolSerializer : KSerializer<Boolean> {
|
||||
|
||||
override fun deserialize(decoder: Decoder): Boolean {
|
||||
val json = (decoder as? JsonInput)?.decodeJson()
|
||||
val bool = json?.primitive?.booleanOrNull
|
||||
if (bool != null)
|
||||
return bool
|
||||
val int = json?.primitive?.intOrNull
|
||||
?: throw SerializationException("boolean or int required")
|
||||
return int != 0
|
||||
}
|
||||
override val descriptor =
|
||||
PrimitiveSerialDescriptor(IntToBoolSerializer::class.qualifiedName!!, PrimitiveKind.STRING)
|
||||
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = SerialDescriptor(IntToBoolSerializer::class.qualifiedName!!)
|
||||
override fun deserialize(decoder: Decoder): Boolean =
|
||||
decoder.decodeString().toBooleanFromInt()
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Boolean) {
|
||||
encoder.encodeBoolean(value)
|
||||
}
|
||||
|
||||
private fun String.toBooleanFromInt() =
|
||||
toBoolean() || toIntOrNull()?.let { it > 0 } ?: false
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ internal class ProtonApiBackendTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `test deserialize bool from int`() = runBlocking {
|
||||
fun `can deserialize false from 0`() = runBlocking {
|
||||
webServer.prepareResponse(
|
||||
HttpURLConnection.HTTP_OK,
|
||||
"""{ "Number": 5, "String": "foo", Bool: 0 }"""
|
||||
|
@ -250,6 +250,26 @@ internal class ProtonApiBackendTests {
|
|||
assertEquals(false, result.valueOrNull?.bool)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can deserialize true from 1`() = runBlocking {
|
||||
webServer.prepareResponse(
|
||||
HttpURLConnection.HTTP_OK,
|
||||
"""{ "Number": 5, "String": "foo", Bool: 1 }""")
|
||||
|
||||
val result = backend(ApiManager.Call(0) { test() })
|
||||
assertEquals(true, result.valueOrNull?.bool)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `can deserialize true from 5`() = runBlocking {
|
||||
webServer.prepareResponse(
|
||||
HttpURLConnection.HTTP_OK,
|
||||
"""{ "Number": 5, "String": "foo", Bool: 5 }""")
|
||||
|
||||
val result = backend(ApiManager.Call(0) { test() })
|
||||
assertEquals(true, result.valueOrNull?.bool)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test pinning error`() = runBlocking {
|
||||
val badBackend = createBackend {
|
||||
|
|
|
@ -37,7 +37,6 @@ pluginManagement {
|
|||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||
maven("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.util.android.sharedpreferences
|
||||
|
||||
import me.proton.core.test.android.mocks.mockSharedPreferences
|
||||
import me.proton.core.util.android.sharedpreferences.internal.SerializableTestChild
|
||||
import me.proton.core.util.android.sharedpreferences.internal.SerializableTestClass
|
||||
import me.proton.core.util.kotlin.startsWith
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFails
|
||||
import me.proton.core.util.kotlin.startsWith
|
||||
|
||||
/**
|
||||
* Test suite for serializable items within SharedPreferences
|
||||
|
@ -41,14 +59,15 @@ internal class SerializableTest {
|
|||
fun `proper message is displayed if given class is not serializable`() {
|
||||
|
||||
// GIVEN
|
||||
val ns =
|
||||
NonSerializableTestClass()
|
||||
val ns = NonSerializableTestClass()
|
||||
|
||||
// WHEN
|
||||
val block = { p["key2"] = ns }
|
||||
|
||||
// THEN
|
||||
val message = assertFails(block).localizedMessage
|
||||
assert(message startsWith "Can't locate argument-less serializer for class NonSerializableTestClass.")
|
||||
assert(message startsWith "Serializer for class 'NonSerializableTestClass' is not found.") {
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import androidx.work.ListenableWorker
|
|||
import androidx.work.WorkInfo
|
||||
import androidx.work.workDataOf
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import me.proton.core.util.kotlin.deserializeOrNull
|
||||
|
@ -45,8 +44,7 @@ internal const val SERIALIZED_DATA_KEY = "serialized_data_key"
|
|||
* @return [T] deserialized from receiver [WorkInfo.mOutputData]
|
||||
*
|
||||
* @param T must be a class annotate with [Serializable]
|
||||
* @param deserializer optional [DeserializationStrategy] of [T], if no value is passed, the
|
||||
* [ImplicitReflectionSerializer] will be used
|
||||
* @param deserializer optional [DeserializationStrategy] of [T]
|
||||
*
|
||||
* @throws KotlinNullPointerException if no serialized data in present in [Data]
|
||||
*/
|
||||
|
@ -58,8 +56,7 @@ inline fun <reified T : Any> WorkInfo.outputData(
|
|||
* @return [T] deserialized from receiver [Data]
|
||||
*
|
||||
* @param T must be a class annotate with [Serializable]
|
||||
* @param deserializer optional [DeserializationStrategy] of [T], if no value is passed, the
|
||||
* [ImplicitReflectionSerializer] will be used
|
||||
* @param deserializer optional [DeserializationStrategy] of [T]
|
||||
*
|
||||
* @throws KotlinNullPointerException if no serialized data in present in [Data]
|
||||
*/
|
||||
|
@ -78,8 +75,7 @@ inline fun <reified T : Any> Data.deserialize(
|
|||
* @return [Data] created by serializing receiver [T]
|
||||
*
|
||||
* @param T must be a class annotate with [Serializable]
|
||||
* @param serializer optional [SerializationStrategy] of [T], if no value is passed, the
|
||||
* [ImplicitReflectionSerializer] will be used
|
||||
* @param serializer optional [SerializationStrategy] of [T]
|
||||
*/
|
||||
inline fun <reified T : Any> T.toWorkData(
|
||||
serializer: SerializationStrategy<T>? = null
|
||||
|
@ -89,8 +85,7 @@ inline fun <reified T : Any> T.toWorkData(
|
|||
* @return [T] deserialized from [ListenableWorker.getInputData]
|
||||
*
|
||||
* @param T must be a class annotate with [Serializable]
|
||||
* @param deserializer optional [DeserializationStrategy] of [T], if no value is passed, the
|
||||
* [ImplicitReflectionSerializer] will be used
|
||||
* @param deserializer optional [DeserializationStrategy] of [T]
|
||||
*
|
||||
* @throws KotlinNullPointerException if no serialized data in present in [Data]
|
||||
*/
|
||||
|
|
|
@ -52,5 +52,5 @@ repositories {
|
|||
dependencies {
|
||||
implementation(gradleApi())
|
||||
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.14.1")
|
||||
implementation("studio.forface.easygradle:dsl:2.1")
|
||||
implementation("studio.forface.easygradle:dsl:2.7")
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
plugins {
|
||||
val kotlinVersion = "1.4.10"
|
||||
val easyGradle = "0.1"
|
||||
val easyGradle = "2.7"
|
||||
|
||||
kotlin("jvm") version kotlinVersion
|
||||
id("studio.forface.easygradle") version easyGradle
|
||||
|
|
|
@ -72,5 +72,5 @@ dependencies {
|
|||
implementation(gradleApi())
|
||||
implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.4.10")
|
||||
implementation("com.gradle.publish:plugin-publish-plugin:0.12.0")
|
||||
implementation("studio.forface.easygradle:dsl:2.1")
|
||||
implementation("studio.forface.easygradle:dsl:2.7")
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package me.proton.core.util.kotlin
|
|||
|
||||
import kotlinx.serialization.StringFormat
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
|
||||
/**
|
||||
* Configuration for Proton Core library
|
||||
|
@ -29,9 +28,8 @@ import kotlinx.serialization.json.JsonConfiguration
|
|||
object ProtonCoreConfig : Invokable {
|
||||
|
||||
/** Default [StringFormat] for serialize and deserialize JSON strings */
|
||||
var defaultJsonStringFormat: StringFormat = Json(
|
||||
JsonConfiguration.Stable.copy(
|
||||
ignoreUnknownKeys = true, isLenient = true
|
||||
)
|
||||
)
|
||||
var defaultJsonStringFormat: StringFormat = Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,35 @@
|
|||
@file:Suppress(
|
||||
"EXPERIMENTAL_API_USAGE" // Explicit serializer
|
||||
)
|
||||
@file:OptIn(ImplicitReflectionSerializer::class)
|
||||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
* This file is part of Proton Technologies AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.util.kotlin
|
||||
|
||||
import kotlinx.serialization.Decoder
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.Encoder
|
||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.PrimitiveDescriptor
|
||||
import kotlinx.serialization.PrimitiveKind
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.parse
|
||||
import kotlinx.serialization.parseList
|
||||
import kotlinx.serialization.parseMap
|
||||
import kotlinx.serialization.stringify
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import me.proton.core.util.kotlin.ProtonCoreConfig.defaultJsonStringFormat
|
||||
|
||||
/*
|
||||
|
@ -49,7 +59,10 @@ annotation class NeedSerializable
|
|||
@NeedSerializable
|
||||
inline fun <reified T : Any> String.deserialize(
|
||||
deserializer: DeserializationStrategy<T>? = null
|
||||
): T = deserializer?.let { Serializer.parse(deserializer, this) } ?: Serializer.parse(this)
|
||||
): T =
|
||||
deserializer
|
||||
?.let { Serializer.decodeFromString(deserializer, this) }
|
||||
?: Serializer.decodeFromString(this)
|
||||
|
||||
/**
|
||||
* @return [T] object from the receiver [String] or null if receiver can't be deserialized to [T].
|
||||
|
@ -71,7 +84,7 @@ inline fun <reified T : Any> String.deserializeOrNull(
|
|||
* This uses reflection: TODO improve for avoid it
|
||||
*/
|
||||
@NeedSerializable
|
||||
inline fun <reified T : Any> String.deserializeList(): List<T> = Serializer.parseList(this)
|
||||
inline fun <reified T : Any> String.deserializeList(): List<T> = Serializer.decodeFromString(this)
|
||||
|
||||
/**
|
||||
* @return [Map] of [T], [V] object from the receiver [String]
|
||||
|
@ -79,7 +92,7 @@ inline fun <reified T : Any> String.deserializeList(): List<T> = Serializer.pars
|
|||
*/
|
||||
@NeedSerializable
|
||||
inline fun <reified T : Any, reified V : Any> String.deserializeMap(): Map<T, V> =
|
||||
Serializer.parseMap(this)
|
||||
Serializer.decodeFromString(this)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -91,21 +104,21 @@ inline fun <reified T : Any, reified V : Any> String.deserializeMap(): Map<T, V>
|
|||
@NeedSerializable
|
||||
inline fun <reified T : Any> T.serialize(
|
||||
serializer: SerializationStrategy<T>? = null
|
||||
) = serializer?.let { Serializer.stringify(serializer, this) } ?: Serializer.stringify(this)
|
||||
) = serializer?.let { Serializer.encodeToString(serializer, this) } ?: Serializer.encodeToString(this)
|
||||
|
||||
/**
|
||||
* @return [String] from the receiver [List] of [T] object
|
||||
* This uses reflection: TODO improve for avoid it
|
||||
*/
|
||||
@NeedSerializable
|
||||
inline fun <reified T : Any> List<T>.serialize() = Serializer.stringify(this)
|
||||
inline fun <reified T : Any> List<T>.serialize() = Serializer.encodeToString(this)
|
||||
|
||||
/**
|
||||
* @return [String] from the receiver [Map] of [T] and [V] object
|
||||
* This uses reflection: TODO improve for avoid it
|
||||
*/
|
||||
@NeedSerializable
|
||||
inline fun <reified T : Any, reified V : Any> Map<T, V>.serialize() = Serializer.stringify(this)
|
||||
inline fun <reified T : Any, reified V : Any> Map<T, V>.serialize() = Serializer.encodeToString(this)
|
||||
|
||||
|
||||
@PublishedApi
|
||||
|
@ -126,7 +139,7 @@ internal sealed class SerializableTestSealedClass(val value: Int) {
|
|||
class Two(value: Int) : SerializableTestSealedClass(value)
|
||||
|
||||
companion object {
|
||||
fun build(value: Int) : SerializableTestSealedClass {
|
||||
fun build(value: Int): SerializableTestSealedClass {
|
||||
return when (value) {
|
||||
1 -> One(value)
|
||||
2 -> Two(value)
|
||||
|
@ -137,7 +150,7 @@ internal sealed class SerializableTestSealedClass(val value: Int) {
|
|||
@Suppress("ClassName", "ClassNaming") // Test class
|
||||
@Serializer(forClass = SerializableTestSealedClass::class)
|
||||
object _Serializer : KSerializer<SerializableTestSealedClass> {
|
||||
override val descriptor = PrimitiveDescriptor("TestCustomSerializer", PrimitiveKind.STRING)
|
||||
override val descriptor = PrimitiveSerialDescriptor("TestCustomSerializer", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: SerializableTestSealedClass) {
|
||||
val raw = Raw(value.value)
|
||||
|
|
Loading…
Reference in New Issue