diff --git a/app/lint.xml b/android-lint.xml similarity index 100% rename from app/lint.xml rename to android-lint.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3fdeef1b9..5e3e54e3d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,7 +1,7 @@ plugins { id("com.android.application") kotlin("android") - kotlin("plugin.serialization") + kotlin("plugin.serialization") version Plugins.Versions.kotlin kotlin("kapt") } @@ -19,6 +19,9 @@ android { setProperty("archivesBaseName", "jellyfin-androidtv-v$versionName") } + sourceSets["main"].java.srcDirs("src/main/kotlin") + sourceSets["test"].java.srcDirs("src/test/kotlin") + buildFeatures { viewBinding = true } @@ -27,11 +30,6 @@ android { isCoreLibraryDesugaringEnabled = true } - lintOptions { - isAbortOnError = false - sarifReport = true - } - buildTypes { val release by getting { isMinifyEnabled = false @@ -41,15 +39,6 @@ android { // Set flavored application name resValue("string", "app_name", "@string/app_name_release") - - signingConfig = createReleaseSigningConfig()?.let { config -> - signingConfigs.create("release") { - storeFile = config.storeFile - storePassword = config.storePassword - keyAlias = config.keyAlias - keyPassword = config.keyPassword - } - } } val debug by getting { @@ -63,6 +52,12 @@ android { resValue("string", "app_name", "@string/app_name_debug") } } + + lintOptions { + lintConfig = file("$rootDir/android-lint.xml") + isAbortOnError = false + sarifReport = true + } } val versionTxt by tasks.registering { @@ -76,12 +71,11 @@ val versionTxt by tasks.registering { } dependencies { - // Jellyfin apiclient - implementation("com.github.jellyfin.jellyfin-sdk-kotlin:android:v0.7.10") - // Jellyfin SDK - val sdkVersion = findProperty("sdk.version")?.toString() - implementation("org.jellyfin.sdk:jellyfin-platform-android:1.0.0") { + // Jellyfin apiclient & SDK + implementation(libs.jellyfin.apiclient) + implementation(libs.jellyfin.sdk) { // Change version if desired + val sdkVersion = findProperty("sdk.version")?.toString() when (sdkVersion) { "local" -> version { strictly("latest-SNAPSHOT") } "snapshot" -> version { strictly("master-SNAPSHOT") } @@ -90,71 +84,55 @@ dependencies { } // Kotlin - val kotlinxCoroutinesVersion = "1.4.3" - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion") - - val kotlinxSerializationVersion = "1.1.0" - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") + implementation(libs.kotlinx.coroutines) + implementation(libs.kotlinx.serialization.json) // Android(x) - implementation("androidx.core:core-ktx:1.3.2") - implementation("androidx.activity:activity-ktx:1.2.2") - implementation("androidx.fragment:fragment-ktx:1.3.2") - val androidxLeanbackVersion = "1.1.0-beta01" - implementation("androidx.leanback:leanback:${androidxLeanbackVersion}") - implementation("androidx.leanback:leanback-preference:${androidxLeanbackVersion}") - val androidxPreferenceVersion = "1.1.1" - implementation("androidx.preference:preference-ktx:$androidxPreferenceVersion") - implementation("androidx.appcompat:appcompat:1.2.0") - implementation("androidx.tvprovider:tvprovider:1.1.0-alpha01") - implementation("androidx.constraintlayout:constraintlayout:2.0.4") - implementation("androidx.recyclerview:recyclerview:1.1.0") - implementation("androidx.work:work-runtime-ktx:2.5.0") - val androidxLifecycleVersion = "2.3.1" - implementation("androidx.lifecycle:lifecycle-runtime-ktx:$androidxLifecycleVersion") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$androidxLifecycleVersion") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:$androidxLifecycleVersion") - implementation("androidx.lifecycle:lifecycle-service:$androidxLifecycleVersion") - implementation("androidx.window:window:1.0.0-alpha05") - implementation("androidx.cardview:cardview:1.0.0") + implementation(libs.androidx.core) + implementation(libs.androidx.activity) + implementation(libs.androidx.fragment) + implementation(libs.androidx.leanback.core) + implementation(libs.androidx.leanback.preference) + implementation(libs.androidx.preference) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.tvprovider) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.recyclerview) + implementation(libs.androidx.work.runtime) + implementation(libs.bundles.androidx.lifecycle) + implementation(libs.androidx.window) + implementation(libs.androidx.cardview) // Dependency Injection - val koinVersion = "2.2.3" - implementation("io.insert-koin:koin-android:$koinVersion") - implementation("io.insert-koin:koin-androidx-viewmodel:$koinVersion") - implementation("io.insert-koin:koin-androidx-fragment:$koinVersion") + implementation(libs.bundles.koin) // GSON - implementation("com.google.code.gson:gson:2.8.6") + implementation(libs.gson) // Media players - implementation("com.google.android.exoplayer:exoplayer:2.14.0") - implementation("org.videolan.android:libvlc-all:3.3.14") + implementation(libs.exoplayer) + implementation(libs.libvlc) // Image utility - val glideVersion = "4.12.0" - implementation("com.github.bumptech.glide:glide:$glideVersion") - kapt("com.github.bumptech.glide:compiler:$glideVersion") - implementation("com.flaviofaria:kenburnsview:1.0.7") + implementation(libs.glide.core) + kapt(libs.glide.compiler) + implementation(libs.kenburnsview) // Crash Reporting - val acraVersion = "5.7.0" - implementation("ch.acra:acra-http:$acraVersion") - implementation("ch.acra:acra-dialog:$acraVersion") - implementation("ch.acra:acra-limiter:$acraVersion") + implementation(libs.bundles.acra) // Logging - implementation("com.jakewharton.timber:timber:4.7.1") - implementation("uk.uuid.slf4j:slf4j-android:1.7.30-0") + implementation(libs.timber) + implementation(libs.slf4j.android) // Debugging if (getProperty("leakcanary.enable")?.toBoolean() == true) - debugImplementation("com.squareup.leakcanary:leakcanary-android:2.6") + debugImplementation(libs.leakcanary) // Compatibility (desugaring) - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") + coreLibraryDesugaring(libs.android.desugar) // Testing - testImplementation("junit:junit:4.13.1") - testImplementation("org.mockito:mockito-core:3.2.4") + testImplementation(libs.junit) + testImplementation(libs.mockito) } diff --git a/build.gradle.kts b/build.gradle.kts index e8b4e965d..078c1dfaf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,56 +1,29 @@ -buildscript { - repositories { - mavenCentral() - google() - } - - dependencies { - val kotlinVersion = getProperty("kotlin.version") - classpath("com.android.tools.build:gradle:4.2.1") - classpath(kotlin("gradle-plugin", kotlinVersion)) - classpath(kotlin("serialization", kotlinVersion)) - } +plugins { + id("io.gitlab.arturbosch.detekt").version(Plugins.Versions.detekt) } allprojects { - // Dependencies - repositories { - mavenCentral() - google() - // Jellyfin SDK - mavenLocal { - content { - includeVersionByRegex("org.jellyfin.sdk", ".*", "latest-SNAPSHOT") - } - } - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") { - content { - includeVersionByRegex("org.jellyfin.sdk", ".*", "master-SNAPSHOT") - includeVersionByRegex("org.jellyfin.sdk", ".*", "openapi-unstable-SNAPSHOT") - } - } - // Jellyfin apiclient - maven("https://jitpack.io") { - content { - // Only allow legacy apiclient - includeVersionByRegex("com.github.jellyfin.jellyfin-sdk-kotlin", ".*", "v0.7.10") - } - } + repositories.defaultRepositories() +} + +buildscript { + repositories.defaultRepositories() + + dependencies { + classpath(Plugins.androidBuildTools) + classpath(Plugins.kotlin) } } -plugins { - id("io.gitlab.arturbosch.detekt").version("1.17.1") -} // Detekt configuration subprojects { - plugins.apply("io.gitlab.arturbosch.detekt") + apply() detekt { buildUponDefaultConfig = true ignoreFailures = true - config = files("$rootDir/detekt.yml") + config = files("$rootDir/detekt.yaml") basePath = rootDir.absolutePath reports { diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt new file mode 100644 index 000000000..d8b355bca --- /dev/null +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -0,0 +1,39 @@ +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.kotlin.dsl.maven + +fun RepositoryHandler.defaultRepositories() { + mavenCentral() + google() + + // Jellyfin SDK + mavenLocal { + content { + includeVersionByRegex("org.jellyfin.sdk", ".*", "latest-SNAPSHOT") + } + } + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") { + content { + includeVersionByRegex("org.jellyfin.sdk", ".*", "master-SNAPSHOT") + includeVersionByRegex("org.jellyfin.sdk", ".*", "openapi-unstable-SNAPSHOT") + } + } + + // Jellyfin apiclient + maven("https://jitpack.io") { + content { + // Only allow legacy apiclient + includeVersionByRegex("com.github.jellyfin.jellyfin-sdk-kotlin", ".*", "v0.7.10") + } + } +} + +object Plugins { + object Versions { + const val kotlin = "1.5.10" + const val detekt = "1.17.1" + const val androidBuildTools = "4.2.1" + } + + const val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" + const val androidBuildTools = "com.android.tools.build:gradle:${Versions.androidBuildTools}" +} diff --git a/buildSrc/src/main/kotlin/Signing.kt b/buildSrc/src/main/kotlin/Signing.kt deleted file mode 100644 index e996b1aa2..000000000 --- a/buildSrc/src/main/kotlin/Signing.kt +++ /dev/null @@ -1,43 +0,0 @@ -import org.gradle.api.Project -import java.io.File -import java.util.* - -fun Project.createReleaseSigningConfig(): SigningConfigData? { - val serializedKeystore = getProperty("keystore") ?: return null - val storeFile = File.createTempFile("jf", "keystore").apply { - writeBytes(Base64.getDecoder().decode(serializedKeystore)) - } - - val storePassword = getProperty("keystore.password") ?: return null - val keyAlias = getProperty("key.alias") ?: return null - val keyPassword = getProperty("key.password") ?: return null - - return SigningConfigData( - storeFile, - storePassword, - keyAlias, - keyPassword - ) -} - -data class SigningConfigData( - /** - * Store file used when signing. - */ - val storeFile: File, - - /** - * Store password used when signing. - */ - val storePassword: String, - - /** - * Key alias used when signing. - */ - val keyAlias: String, - - /** - * Key password used when signing. - */ - val keyPassword: String, -) diff --git a/detekt.yml b/detekt.yaml similarity index 100% rename from detekt.yml rename to detekt.yaml diff --git a/gradle.properties b/gradle.properties index 38eae72e9..c1b171de3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,10 +8,6 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m # - "unstable-snapshot" (SDK with unstable Jellyfin API) sdk.version=default -# Kotlin -kotlin.version=1.5.0 -kotlin.incremental=true - # Android android.enableJetifier=true android.useAndroidX=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..4c0c2c155 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,91 @@ +[versions] +kotlinx-coroutines = "1.4.3" +kotlinx-serialization = "1.1.0" +androidx-leanback = "1.1.0-beta01" +androidx-lifecycle = "2.3.1" +koin = "2.2.3" +glide = "4.12.0" +acra = "5.7.0" + +[libraries] +# Jellyfin apiclient +jellyfin-apiclient = { module = "com.github.jellyfin.jellyfin-sdk-kotlin:android", version = "v0.7.10" } +# Jellyfin SDK +jellyfin-sdk = { module = "org.jellyfin.sdk:jellyfin-platform-android", version = "1.0.0" } + +# Kotlin +kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } + +# Android(x) +androidx-core = { module = "androidx.core:core-ktx", version = "1.3.2" } +androidx-activity = { module = "androidx.activity:activity-ktx", version = "1.2.2" } +androidx-fragment = { module = "androidx.fragment:fragment-ktx", version = "1.3.2" } +androidx-leanback-core = { module = "androidx.leanback:leanback", version.ref = "androidx-leanback" } +androidx-leanback-preference = { module = "androidx.leanback:leanback-preference", version.ref = "androidx-leanback" } +androidx-preference = { module = "androidx.preference:preference-ktx", version = "1.1.1" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version = "1.2.0" } +androidx-tvprovider = { module = "androidx.tvprovider:tvprovider", version = "1.1.0-alpha01" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.4" } +androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version = "1.1.0" } +androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version = "2.5.0" } +androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-livedata = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidx-lifecycle" } +androidx-lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "androidx-lifecycle" } +androidx-window = { module = "androidx.window:window", version = "1.0.0-alpha05" } +androidx-cardview = { module = "androidx.cardview:cardview", version = "1.0.0" } + +# Dependency Injection +koin-android-core = { module = "io.insert-koin:koin-android", version.ref = "koin" } +koin-android-viewmodel = { module = "io.insert-koin:koin-androidx-viewmodel", version.ref = "koin" } +koin-android-fragment = { module = "io.insert-koin:koin-androidx-fragment", version.ref = "koin" } + +# GSON +gson = { module = "com.google.code.gson:gson", version = "2.8.6" } + +# Media players +exoplayer = { module = "com.google.android.exoplayer:exoplayer", version = "2.14.0" } +libvlc = { module = "org.videolan.android:libvlc-all", version = "3.3.14" } + +# Image utility +glide-core = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } +glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" } +kenburnsview = { module = "com.flaviofaria:kenburnsview", version = "1.0.7" } + +# Crash Reporting +acra-http = { module = "ch.acra:acra-http", version.ref = "acra" } +acra-dialog = { module = "ch.acra:acra-dialog", version.ref = "acra" } +acra-limiter = { module = "ch.acra:acra-limiter", version.ref = "acra" } + +# Logging +timber = { module = "com.jakewharton.timber:timber", version = "4.7.1" } +slf4j-android = { module = "uk.uuid.slf4j:slf4j-android", version = "1.7.30-0" } + +# Debugging +leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version = "2.6" } + +# Compatibility (desugaring) +android-desugar = { module = "com.android.tools:desugar_jdk_libs", version = "1.1.5" } + +# Testing +junit = { module = "junit:junit", version = "4.13.1" } +mockito = { module = "org.mockito:mockito-core", version = "3.2.4" } + +[bundles] +androidx-lifecycle = [ + "androidx-lifecycle-runtime", + "androidx-lifecycle-viewmodel", + "androidx-lifecycle-livedata", + "androidx-lifecycle-service", +] +koin = [ + "koin-android-core", + "koin-android-viewmodel", + "koin-android-fragment", +] +acra = [ + "acra-http", + "acra-dialog", + "acra-limiter", +] diff --git a/settings.gradle.kts b/settings.gradle.kts index da1266909..99e7dc017 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,23 +1,5 @@ -import java.util.* +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +enableFeaturePreview("VERSION_CATALOGS") +// Application include(":app") - -// Load properties from local.properties -val properties = Properties().apply { - val location = File("local.properties") - if (location.exists()) - load(location.inputStream()) -} - -// Get value for dependency substitution -val enableDependencySubstitution = properties.getProperty("enable.dependency.substitution", "true").equals("true", true) - -// Replace apiclient dependency with local version -val apiclientLocation = "../jellyfin-apiclient-java" -if (File(apiclientLocation).exists() && enableDependencySubstitution) { - includeBuild(apiclientLocation) { - dependencySubstitution { - substitute(module("org.jellyfin.apiclient:android")).with(project(":android")) - } - } -}