diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..1e1d8a0 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,19 @@ +name: Build and test + +on: [push, pull_request] + +jobs: + test: + name: Build and test + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Debug build + run: ./gradlew assembleDebug --stacktrace + - name: Unit test + run: ./gradlew testDebugUnitTest --stacktrace diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..42c12d5 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,34 @@ +name: Publish + +on: + push: + tags: + - "*-RELEASE" + +jobs: + publish: + name: Release build and publish + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Create secrets file + env: + GPG_KEY_CONTENTS: ${{ secrets.GPG_KEY_CONTENTS }} + SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }} + run: sudo bash -c "echo '$GPG_KEY_CONTENTS' | base64 -d > '$SIGNING_SECRET_KEY_RING_FILE'" + - name: Release build and source jar generation + run: ./gradlew :krate:assembleRelease :krate-gson:assembleRelease :krate-moshi-stub:assembleRelease :krate-moshi-core:assembleRelease :krate-moshi-reflect:assembleRelease :krate-moshi-codegen:assembleRelease :krate:androidSourcesJar :krate-gson:androidSourcesJar :krate-moshi-stub:androidSourcesJar :krate-moshi-core:androidSourcesJar :krate-moshi-reflect:androidSourcesJar :krate-moshi-codegen:androidSourcesJar + - name: Publish to MavenCentral + run: ./gradlew publishReleasePublicationToSonatypeRepository closeAndReleaseRepository + env: + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + SIGNING_SECRET_KEY_RING_FILE: ${{ secrets.SIGNING_SECRET_KEY_RING_FILE }} + SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} diff --git a/README.md b/README.md index 66b5ae3..0b8ef29 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,25 @@ class MainActivity : AppCompatActivity(), Krate { } ``` +### Third party implementations + +You can create the `SharedPreferences` instance to implement Krate's interface by using third party `SharedPreferences` implementations as well. For example, [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences) or [Harmony](https://github.com/pablobaxter/Harmony) are such implementations. + +Here's how you'd use EncryptedSharedPreferences with Krate ([see this source file for the full code](hu/autsoft/krateexample/krates/ExampleThirdPartyKrates.kt)): + +```kotlin +class EncryptedKrate(applicationContext: Context) : Krate { + override val sharedPreferences: SharedPreferences + + init { + /* ... */ + sharedPreferences = EncryptedSharedPreferences.create(applicationContext, ...) + } + + val myStringValue: String by stringPref("my_string_value", "") +} +``` + # Validation You can add validation rules to your Krate properties by providing an additional lambda parameter, `isValid`: diff --git a/app/build.gradle b/app/build.gradle index 57121ed..cad617e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ android { compileSdkVersion compile_sdk defaultConfig { applicationId "hu.autsoft.krateexample" - minSdkVersion min_sdk + minSdkVersion 21 targetSdkVersion target_sdk versionCode 1 versionName "1.0" @@ -21,9 +21,13 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.google.android.material:material:1.0.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + + // Third-party SharedPreferences implementations + implementation "androidx.security:security-crypto:1.1.0-alpha02" + implementation 'com.frybits.harmony:harmony:1.1.2' implementation project(path: ':krate') implementation project(path: ':krate-gson') diff --git a/app/src/main/java/hu/autsoft/krateexample/krates/ExampleThirdPartyKrates.kt b/app/src/main/java/hu/autsoft/krateexample/krates/ExampleThirdPartyKrates.kt new file mode 100644 index 0000000..80b0a23 --- /dev/null +++ b/app/src/main/java/hu/autsoft/krateexample/krates/ExampleThirdPartyKrates.kt @@ -0,0 +1,46 @@ +package hu.autsoft.krateexample.krates + +import android.content.Context +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import com.frybits.harmony.getHarmonySharedPreferences +import hu.autsoft.krate.Krate +import hu.autsoft.krate.stringPref + +/** + * Encrypted Krate based on the AndroidX EncryptedSharedPreferences class. + */ +class EncryptedKrate(applicationContext: Context) : Krate { + override val sharedPreferences: SharedPreferences + + init { + // Check the official docs on how to create EncryptedPreferences instances + // https://developer.android.com/topic/security/data + // https://developer.android.com/reference/kotlin/androidx/security/crypto/EncryptedSharedPreferences + val masterKeyAlias = MasterKey.Builder(applicationContext) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + val sharedPrefsFile = "my_sensitive_data.txt" + + sharedPreferences = EncryptedSharedPreferences.create( + applicationContext, + sharedPrefsFile, + masterKeyAlias, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + } + + val myStringValue: String by stringPref("my_string_value", "") +} + +/** + * Harmony is a multi-process SharedPreferences implementation. + * See its GitHub page for details https://github.com/pablobaxter/Harmony + */ +class HarmonyKrate(context: Context) : Krate { + override val sharedPreferences: SharedPreferences = context.getHarmonySharedPreferences("PREF_NAME") + + val myStringValue: String by stringPref("my_string_value", "") +} diff --git a/build.gradle b/build.gradle index 7c01d67..e601658 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,9 @@ buildscript { - ext.kotlin_version = '1.3.61' + ext.kotlin_version = '1.4.10' ext.krate_version = '0.4.0' - ext.moshi_version = '1.9.2' + ext.moshi_version = '1.9.3' + ext.junit_version = '4.13' ext.min_sdk = 15 ext.compile_sdk = 29 @@ -15,7 +16,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.21.0" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ce9b16c..14e30f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Feb 09 16:52:49 CET 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/krate-gson/build.gradle b/krate-gson/build.gradle index 0324a61..5c1f5b9 100644 --- a/krate-gson/build.gradle +++ b/krate-gson/build.gradle @@ -16,12 +16,22 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + '-Xopt-in=kotlin.RequiresOptIn', + ] + } +} + dependencies { implementation project(':krate') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" api 'com.google.code.gson:gson:2.8.5' - testImplementation 'junit:junit:4.12' + testImplementation "junit:junit:$junit_version" testImplementation 'org.robolectric:robolectric:4.3.1' } diff --git a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/Functions.kt b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/Functions.kt index 095e720..ddfad47 100644 --- a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/Functions.kt +++ b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/Functions.kt @@ -1,4 +1,4 @@ -@file:Suppress("RedundantVisibilityModifier", "unused") +@file:Suppress("unused") package hu.autsoft.krate.gson diff --git a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/GsonInstances.kt b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/GsonInstances.kt index 8eab747..06af48e 100644 --- a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/GsonInstances.kt +++ b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/GsonInstances.kt @@ -15,7 +15,7 @@ internal inline val Krate.internalGson: Gson * * Set to null to use the default instance. */ -var Krate.gson: Gson? +public var Krate.gson: Gson? set(value) { if (value != null) { gsonInstances[this] = value diff --git a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/ValidatedFunctions.kt b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/ValidatedFunctions.kt index 1062984..a18ab81 100644 --- a/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/ValidatedFunctions.kt +++ b/krate-gson/src/main/kotlin/hu/autsoft/krate/gson/ValidatedFunctions.kt @@ -1,4 +1,4 @@ -@file:Suppress("RedundantVisibilityModifier", "unused") +@file:[Suppress("unused") OptIn(InternalKrateApi::class)] package hu.autsoft.krate.gson @@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken import hu.autsoft.krate.Krate import hu.autsoft.krate.gson.default.GsonDelegateWithDefault import hu.autsoft.krate.gson.optional.GsonDelegate +import hu.autsoft.krate.internal.InternalKrateApi import hu.autsoft.krate.validated.ValidatedPreferenceDelegate import java.lang.reflect.Type import kotlin.properties.ReadWriteProperty diff --git a/krate-gson/src/test/java/hu/autsoft/krate/Util.kt b/krate-gson/src/test/java/hu/autsoft/krate/Util.kt index 173d052..2b429f4 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/Util.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/Util.kt @@ -3,5 +3,5 @@ package hu.autsoft.krate import android.content.Context import androidx.test.platform.app.InstrumentationRegistry -val targetContext: Context +internal val targetContext: Context get() = InstrumentationRegistry.getInstrumentation().targetContext diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/GsonTestKrate.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/GsonTestKrate.kt index e5e95b9..8ba48e7 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/GsonTestKrate.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/GsonTestKrate.kt @@ -4,7 +4,7 @@ import android.content.Context import hu.autsoft.krate.SimpleKrate -class GsonTestKrate(context: Context) : SimpleKrate(context) { +internal class GsonTestKrate(context: Context) : SimpleKrate(context) { init { sharedPreferences.edit().clear().commit() diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/TestModel.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/TestModel.kt index 5702a41..a9d92ba 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/TestModel.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/TestModel.kt @@ -1,3 +1,3 @@ package hu.autsoft.krate.gson -data class TestModel(val x: Int, val y: Double, val z: String) +internal data class TestModel(val x: Int, val y: Double, val z: String) diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/CustomGsonInstanceTest.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/CustomGsonInstanceTest.kt index 61f9d2c..242dbee 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/CustomGsonInstanceTest.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/CustomGsonInstanceTest.kt @@ -14,7 +14,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class CustomGsonInstanceTest { +internal class CustomGsonInstanceTest { private lateinit var krate: Krate diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateTest.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateTest.kt index 1ab84da..f6bbaf4 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateTest.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class GsonDelegateTest { +internal class GsonDelegateTest { private lateinit var krate: GsonTestKrate diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateWithDefaultTest.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateWithDefaultTest.kt index 6cf4581..bf302c6 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateWithDefaultTest.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/GsonDelegateWithDefaultTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class GsonDelegateWithDefaultTest { +internal class GsonDelegateWithDefaultTest { private lateinit var krate: GsonTestKrate diff --git a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/ValidatedGsonDelegateTest.kt b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/ValidatedGsonDelegateTest.kt index 11b659a..bc4014c 100644 --- a/krate-gson/src/test/java/hu/autsoft/krate/gson/test/ValidatedGsonDelegateTest.kt +++ b/krate-gson/src/test/java/hu/autsoft/krate/gson/test/ValidatedGsonDelegateTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class ValidatedGsonDelegateTest { +internal class ValidatedGsonDelegateTest { private lateinit var krate: GsonTestKrate diff --git a/krate-moshi-codegen/build.gradle b/krate-moshi-codegen/build.gradle index 499f177..eb8e02c 100644 --- a/krate-moshi-codegen/build.gradle +++ b/krate-moshi-codegen/build.gradle @@ -17,6 +17,15 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + ] + } +} + dependencies { implementation project(':krate') api project(':krate-moshi-core') @@ -25,7 +34,7 @@ dependencies { api "com.squareup.moshi:moshi:$moshi_version" kaptTest "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" - testImplementation 'junit:junit:4.12' + testImplementation "junit:junit:$junit_version" testImplementation 'org.robolectric:robolectric:4.3.1' } diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt index 6be80a6..146010f 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt @@ -4,7 +4,7 @@ import android.content.Context import hu.autsoft.krate.SimpleKrate -class MoshiTestKrate(context: Context) : SimpleKrate(context) { +internal class MoshiTestKrate(context: Context) : SimpleKrate(context) { init { sharedPreferences.edit().clear().commit() diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/TestModel.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/TestModel.kt index 2abef7f..738893f 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/TestModel.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/TestModel.kt @@ -3,4 +3,4 @@ package hu.autsoft.krate.moshi import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class TestModel(val x: Int, val y: Double, val z: String) +internal data class TestModel(val x: Int, val y: Double, val z: String) diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt index 0e0155b..567a961 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt @@ -14,7 +14,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class CustomMoshiInstanceTest { +internal class CustomMoshiInstanceTest { private lateinit var krate: Krate diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt index f489be6..f4264f6 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class MoshiDelegateTest { +internal class MoshiDelegateTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt index 5e91379..de0293b 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class MoshiDelegateWithDefaultTest { +internal class MoshiDelegateWithDefaultTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt index 682c466..22a0c2d 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt @@ -12,7 +12,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class ValidatedGsonDelegateTest { +internal class ValidatedGsonDelegateTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/util/Util.kt b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/util/Util.kt index 2d21f38..15a6402 100644 --- a/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/util/Util.kt +++ b/krate-moshi-codegen/src/test/java/hu/autsoft/krate/moshi/util/Util.kt @@ -3,5 +3,5 @@ package hu.autsoft.krate.moshi.util import android.content.Context import androidx.test.platform.app.InstrumentationRegistry -val targetContext: Context +internal val targetContext: Context get() = InstrumentationRegistry.getInstrumentation().targetContext diff --git a/krate-moshi-core/build.gradle b/krate-moshi-core/build.gradle index ba6dbff..878e1e2 100644 --- a/krate-moshi-core/build.gradle +++ b/krate-moshi-core/build.gradle @@ -16,6 +16,16 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + '-Xopt-in=kotlin.RequiresOptIn', + ] + } +} + dependencies { implementation project(':krate') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/Functions.kt b/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/Functions.kt index a1e08dc..ff22a6b 100644 --- a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/Functions.kt +++ b/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/Functions.kt @@ -1,20 +1,22 @@ -@file:Suppress("RedundantVisibilityModifier", "unused") +@file:Suppress("unused") package hu.autsoft.krate.moshi import hu.autsoft.krate.Krate import hu.autsoft.krate.moshi.default.MoshiDelegateWithDefault import hu.autsoft.krate.moshi.optional.MoshiDelegate -import hu.autsoft.krate.moshi.util.makeType import java.lang.reflect.Type import kotlin.properties.ReadWriteProperty +import kotlin.reflect.javaType +import kotlin.reflect.typeOf /** * Creates an optional preference of type T with the given [key] in this [Krate] instance. * This instance will be serialized using Moshi. */ +@OptIn(ExperimentalStdlibApi::class) public inline fun Krate.moshiPref(key: String): ReadWriteProperty { - return moshiPrefImpl(key, makeType()) + return moshiPrefImpl(key, typeOf().javaType) } @PublishedApi @@ -26,8 +28,9 @@ internal fun Krate.moshiPrefImpl(key: String, type: Type): ReadWritePr * Creates a non-optional preference of type T with the given [key] and [defaultValue] in this [Krate] instance. * This instance will be serialized using Moshi. */ +@OptIn(ExperimentalStdlibApi::class) public inline fun Krate.moshiPref(key: String, defaultValue: T): ReadWriteProperty { - return moshiPrefImpl(key, defaultValue, makeType()) + return moshiPrefImpl(key, defaultValue, typeOf().javaType) } @PublishedApi diff --git a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/MoshiInstances.kt b/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/MoshiInstances.kt index 1b961e4..7284f1e 100644 --- a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/MoshiInstances.kt +++ b/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/MoshiInstances.kt @@ -14,12 +14,12 @@ internal val moshiInstances: MutableMap = mutableMapOf Krate.moshiPref( key: String, noinline isValid: (newValue: T?) -> Boolean ): ReadWriteProperty { - return moshiPrefImpl(key, makeType(), isValid) + return moshiPrefImpl(key, typeOf().javaType, isValid) } @PublishedApi @@ -35,12 +38,13 @@ internal fun Krate.moshiPrefImpl( * in this [Krate] instance. * This value will be serialized using Moshi. */ +@OptIn(ExperimentalStdlibApi::class) public inline fun Krate.moshiPref( key: String, defaultValue: T, noinline isValid: (newValue: T) -> Boolean ): ReadWriteProperty { - return moshiPrefImpl(key, defaultValue, makeType(), isValid) + return moshiPrefImpl(key, defaultValue, typeOf().javaType, isValid) } @PublishedApi diff --git a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/util/TypeToken.kt b/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/util/TypeToken.kt deleted file mode 100644 index 580cd8d..0000000 --- a/krate-moshi-core/src/main/kotlin/hu/autsoft/krate/moshi/util/TypeToken.kt +++ /dev/null @@ -1,19 +0,0 @@ -package hu.autsoft.krate.moshi.util - -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -@PublishedApi -internal abstract class TypeToken - -@PublishedApi -internal inline fun makeType(): Type { - val token = object : TypeToken() {} - - val superclass: Type = requireNotNull(token::class.java.genericSuperclass) - if (superclass is Class<*>) { - throw RuntimeException("Missing type parameter.") - } - val parameterized = superclass as ParameterizedType - return parameterized.actualTypeArguments[0] -} diff --git a/krate-moshi-reflect/build.gradle b/krate-moshi-reflect/build.gradle index e3ff029..23d0f86 100644 --- a/krate-moshi-reflect/build.gradle +++ b/krate-moshi-reflect/build.gradle @@ -16,6 +16,15 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + ] + } +} + dependencies { implementation project(':krate') api project(':krate-moshi-core') @@ -24,7 +33,7 @@ dependencies { api "com.squareup.moshi:moshi:$moshi_version" api "com.squareup.moshi:moshi-kotlin:$moshi_version" - testImplementation 'junit:junit:4.12' + testImplementation "junit:junit:$junit_version" testImplementation 'org.robolectric:robolectric:4.3.1' } diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt index 6be80a6..146010f 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/MoshiTestKrate.kt @@ -4,7 +4,7 @@ import android.content.Context import hu.autsoft.krate.SimpleKrate -class MoshiTestKrate(context: Context) : SimpleKrate(context) { +internal class MoshiTestKrate(context: Context) : SimpleKrate(context) { init { sharedPreferences.edit().clear().commit() diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/TestModel.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/TestModel.kt index fb57c63..3f95526 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/TestModel.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/TestModel.kt @@ -1,3 +1,3 @@ package hu.autsoft.krate.moshi -data class TestModel(val x: Int, val y: Double, val z: String) +internal data class TestModel(val x: Int, val y: Double, val z: String) diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt index 73d6d40..e1691d4 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/CustomMoshiInstanceTest.kt @@ -15,7 +15,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class CustomMoshiInstanceTest { +internal class CustomMoshiInstanceTest { private lateinit var krate: Krate diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt index f489be6..f4264f6 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class MoshiDelegateTest { +internal class MoshiDelegateTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt index 5e91379..de0293b 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/MoshiDelegateWithDefaultTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class MoshiDelegateWithDefaultTest { +internal class MoshiDelegateWithDefaultTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt index 11e0e98..27c7859 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/test/ValidatedGsonDelegateTest.kt @@ -10,7 +10,7 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class ValidatedGsonDelegateTest { +internal class ValidatedGsonDelegateTest { private lateinit var krate: MoshiTestKrate diff --git a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/util/Util.kt b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/util/Util.kt index 2d21f38..15a6402 100644 --- a/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/util/Util.kt +++ b/krate-moshi-reflect/src/test/java/hu/autsoft/krate/moshi/util/Util.kt @@ -3,5 +3,5 @@ package hu.autsoft.krate.moshi.util import android.content.Context import androidx.test.platform.app.InstrumentationRegistry -val targetContext: Context +internal val targetContext: Context get() = InstrumentationRegistry.getInstrumentation().targetContext diff --git a/krate-moshi-stub/build.gradle b/krate-moshi-stub/build.gradle index 7a7ecac..2837009 100644 --- a/krate-moshi-stub/build.gradle +++ b/krate-moshi-stub/build.gradle @@ -16,6 +16,15 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + ] + } +} + dependencies { implementation project(':krate') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/krate-moshi-stub/src/main/kotlin/hu/autsoft/krate/moshi/DefaultMoshiInstance.kt b/krate-moshi-stub/src/main/kotlin/hu/autsoft/krate/moshi/DefaultMoshiInstance.kt index 0f6513f..84f64b3 100644 --- a/krate-moshi-stub/src/main/kotlin/hu/autsoft/krate/moshi/DefaultMoshiInstance.kt +++ b/krate-moshi-stub/src/main/kotlin/hu/autsoft/krate/moshi/DefaultMoshiInstance.kt @@ -2,8 +2,8 @@ package hu.autsoft.krate.moshi import com.squareup.moshi.Moshi -@Suppress("IMPLICIT_NOTHING_AS_TYPE_PARAMETER") -val defaultMoshi: Moshi by lazy { +@Suppress("IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION") +public val defaultMoshi: Moshi by lazy { TODO(""" This is a compile time placeholder so that defaultMoshi can be resolved in the core module. You should include krate-moshi-reflect or krate-moshi-codegen on your classpath for runtime. diff --git a/krate/build.gradle b/krate/build.gradle index 956c0ce..3953c53 100644 --- a/krate/build.gradle +++ b/krate/build.gradle @@ -16,11 +16,21 @@ android { } } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + freeCompilerArgs += [ + '-progressive', + '-Xexplicit-api=strict', + '-Xopt-in=kotlin.RequiresOptIn', + ] + } +} + dependencies { implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.preference:preference:1.1.0' + implementation 'androidx.preference:preference-ktx:1.1.1' - testImplementation 'junit:junit:4.12' + testImplementation "junit:junit:$junit_version" testImplementation 'org.robolectric:robolectric:4.3.1' } diff --git a/krate/src/main/kotlin/hu/autsoft/krate/Functions.kt b/krate/src/main/kotlin/hu/autsoft/krate/Functions.kt index f9c331b..8f3f2c9 100644 --- a/krate/src/main/kotlin/hu/autsoft/krate/Functions.kt +++ b/krate/src/main/kotlin/hu/autsoft/krate/Functions.kt @@ -1,4 +1,4 @@ -@file:Suppress("RedundantVisibilityModifier", "unused") +@file:Suppress("unused") package hu.autsoft.krate diff --git a/krate/src/main/kotlin/hu/autsoft/krate/Krate.kt b/krate/src/main/kotlin/hu/autsoft/krate/Krate.kt index cc6549d..08c63f3 100644 --- a/krate/src/main/kotlin/hu/autsoft/krate/Krate.kt +++ b/krate/src/main/kotlin/hu/autsoft/krate/Krate.kt @@ -1,5 +1,3 @@ -@file:Suppress("RedundantVisibilityModifier") - package hu.autsoft.krate import android.content.SharedPreferences diff --git a/krate/src/main/kotlin/hu/autsoft/krate/SimpleKrate.kt b/krate/src/main/kotlin/hu/autsoft/krate/SimpleKrate.kt index 32e7d9c..2dada8a 100644 --- a/krate/src/main/kotlin/hu/autsoft/krate/SimpleKrate.kt +++ b/krate/src/main/kotlin/hu/autsoft/krate/SimpleKrate.kt @@ -1,5 +1,3 @@ -@file:Suppress("RedundantVisibilityModifier") - package hu.autsoft.krate import android.content.Context diff --git a/krate/src/main/kotlin/hu/autsoft/krate/ValidatedFunctions.kt b/krate/src/main/kotlin/hu/autsoft/krate/ValidatedFunctions.kt index 9be8603..a1cfc62 100644 --- a/krate/src/main/kotlin/hu/autsoft/krate/ValidatedFunctions.kt +++ b/krate/src/main/kotlin/hu/autsoft/krate/ValidatedFunctions.kt @@ -1,4 +1,4 @@ -@file:Suppress("RedundantVisibilityModifier", "unused") +@file:[Suppress("unused") OptIn(InternalKrateApi::class)] package hu.autsoft.krate @@ -7,6 +7,7 @@ import hu.autsoft.krate.default.IntDelegateWithDefault import hu.autsoft.krate.default.LongDelegateWithDefault import hu.autsoft.krate.default.StringDelegateWithDefault import hu.autsoft.krate.default.StringSetDelegateWithDefault +import hu.autsoft.krate.internal.InternalKrateApi import hu.autsoft.krate.optional.FloatDelegate import hu.autsoft.krate.optional.IntDelegate import hu.autsoft.krate.optional.LongDelegate diff --git a/krate/src/main/kotlin/hu/autsoft/krate/internal/InternalKrateApi.kt b/krate/src/main/kotlin/hu/autsoft/krate/internal/InternalKrateApi.kt new file mode 100644 index 0000000..213fa5d --- /dev/null +++ b/krate/src/main/kotlin/hu/autsoft/krate/internal/InternalKrateApi.kt @@ -0,0 +1,8 @@ +package hu.autsoft.krate.internal + +@RequiresOptIn( + message = "This API is meant to be used by Krate's own modules, and third-party extensions to Krate. " + + "If you're a client of the Krate library, you probably shouldn't be using it.", + level = RequiresOptIn.Level.ERROR +) +public annotation class InternalKrateApi diff --git a/krate/src/main/kotlin/hu/autsoft/krate/validated/ValidatedPreferenceDelegate.kt b/krate/src/main/kotlin/hu/autsoft/krate/validated/ValidatedPreferenceDelegate.kt index 531396f..d749a8b 100644 --- a/krate/src/main/kotlin/hu/autsoft/krate/validated/ValidatedPreferenceDelegate.kt +++ b/krate/src/main/kotlin/hu/autsoft/krate/validated/ValidatedPreferenceDelegate.kt @@ -1,6 +1,7 @@ package hu.autsoft.krate.validated import hu.autsoft.krate.Krate +import hu.autsoft.krate.internal.InternalKrateApi import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -11,7 +12,8 @@ import kotlin.reflect.KProperty * @param [delegate] the [ReadWriteProperty] implementation that is used for delegation * @param [isValid] the lambda used to validate property values on [setValue] */ -class ValidatedPreferenceDelegate( +@InternalKrateApi +public class ValidatedPreferenceDelegate( private val delegate: ReadWriteProperty, private val isValid: (newValue: T) -> Boolean ) : ReadWriteProperty by delegate { diff --git a/krate/src/test/java/hu/autsoft/krate/DefaultTests.kt b/krate/src/test/java/hu/autsoft/krate/DefaultTests.kt index cd0e966..38e4be1 100644 --- a/krate/src/test/java/hu/autsoft/krate/DefaultTests.kt +++ b/krate/src/test/java/hu/autsoft/krate/DefaultTests.kt @@ -1,13 +1,13 @@ package hu.autsoft.krate -import junit.framework.Assert.assertEquals +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class DefaultTests { +internal class DefaultTests { private lateinit var testKrate: TestKrate @@ -27,4 +27,4 @@ class DefaultTests { fun testValidatedDefaultFloatDelegateThrowsException() { testKrate.defaultValidatedFloat = 5.0f } -} \ No newline at end of file +} diff --git a/krate/src/test/java/hu/autsoft/krate/OptionalTests.kt b/krate/src/test/java/hu/autsoft/krate/OptionalTests.kt index 526d3e5..6729472 100644 --- a/krate/src/test/java/hu/autsoft/krate/OptionalTests.kt +++ b/krate/src/test/java/hu/autsoft/krate/OptionalTests.kt @@ -8,7 +8,7 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class OptionalTests { +internal class OptionalTests { private lateinit var testKrate: TestKrate diff --git a/krate/src/test/java/hu/autsoft/krate/TestKrate.kt b/krate/src/test/java/hu/autsoft/krate/TestKrate.kt index 8ff79b2..fc4d0e8 100644 --- a/krate/src/test/java/hu/autsoft/krate/TestKrate.kt +++ b/krate/src/test/java/hu/autsoft/krate/TestKrate.kt @@ -2,7 +2,7 @@ package hu.autsoft.krate import android.content.Context -class TestKrate(context: Context) : SimpleKrate(context) { +internal class TestKrate(context: Context) : SimpleKrate(context) { init { sharedPreferences.edit().clear().commit() diff --git a/krate/src/test/java/hu/autsoft/krate/Util.kt b/krate/src/test/java/hu/autsoft/krate/Util.kt index 173d052..2b429f4 100644 --- a/krate/src/test/java/hu/autsoft/krate/Util.kt +++ b/krate/src/test/java/hu/autsoft/krate/Util.kt @@ -3,5 +3,5 @@ package hu.autsoft.krate import android.content.Context import androidx.test.platform.app.InstrumentationRegistry -val targetContext: Context +internal val targetContext: Context get() = InstrumentationRegistry.getInstrumentation().targetContext diff --git a/scripts/publish-mavencentral.gradle b/scripts/publish-mavencentral.gradle index ce44be9..5f3fd4d 100644 --- a/scripts/publish-mavencentral.gradle +++ b/scripts/publish-mavencentral.gradle @@ -1,6 +1,6 @@ task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.source + archiveClassifier.set('sources') + from android.sourceSets.main.java.srcDirs } artifacts { @@ -18,6 +18,7 @@ ext["signing.password"] = '' ext["signing.secretKeyRingFile"] = '' ext["ossrhUsername"] = '' ext["ossrhPassword"] = '' +ext["sonatypeStagingProfileId"] = '' File secretPropsFile = project.rootProject.file('local.properties') if (secretPropsFile.exists()) { @@ -34,11 +35,12 @@ if (secretPropsFile.exists()) { ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') + ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') } nexusStaging { packageGroup = PUBLISH_GROUP_ID - stagingProfileId = 'caceeedcae039c' + stagingProfileId = sonatypeStagingProfileId username = ossrhUsername password = ossrhPassword }