/* * SPDX-FileCopyrightText: 2025 NewPipe e.V. * SPDX-License-Identifier: GPL-3.0-or-later */ import com.mikepenz.aboutlibraries.plugin.DuplicateMode import com.android.build.api.dsl.ApplicationExtension plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) alias(libs.plugins.jetbrains.kotlin.compose) alias(libs.plugins.jetbrains.kotlin.kapt) alias(libs.plugins.jetbrains.kotlin.parcelize) alias(libs.plugins.jetbrains.kotlinx.serialization) alias(libs.plugins.google.ksp) alias(libs.plugins.sonarqube) alias(libs.plugins.hilt) alias(libs.plugins.about.libraries) checkstyle } val gitWorkingBranch = providers.exec { commandLine("git", "rev-parse", "--abbrev-ref", "HEAD") }.standardOutput.asText.map { it.trim() } java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } kotlin { compilerOptions { // TODO: Drop annotation default target when it is stable freeCompilerArgs.addAll( "-Xannotation-default-target=param-property" ) } } configure { compileSdk = 36 namespace = "org.schabi.newpipe" defaultConfig { applicationId = "org.schabi.newpipe" resValue("string", "app_name", "NewPipe") minSdk = 23 targetSdk = 35 versionCode = System.getProperty("versionCodeOverride")?.toInt() ?: 1008 versionName = "0.28.3" System.getProperty("versionNameSuffix")?.let { versionNameSuffix = it } testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { debug { isDebuggable = true // suffix the app id and the app name with git branch name val defaultBranches = listOf("master", "dev") val workingBranch = gitWorkingBranch.getOrElse("") val normalizedWorkingBranch = workingBranch .replaceFirst("^[^A-Za-z]+".toRegex(), "") .replace("[^0-9A-Za-z]+".toRegex(), "") if (normalizedWorkingBranch.isEmpty() || workingBranch in defaultBranches) { // default values when branch name could not be determined or is master or dev applicationIdSuffix = ".debug" resValue("string", "app_name", "NewPipe Debug") } else { applicationIdSuffix = ".debug.$normalizedWorkingBranch" resValue("string", "app_name", "NewPipe $workingBranch") } } release { System.getProperty("packageSuffix")?.let { suffix -> applicationIdSuffix = suffix resValue("string", "app_name", "NewPipe $suffix") } isMinifyEnabled = true isShrinkResources = false // disabled to fix F-Droid"s reproducible build proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } lint { checkReleaseBuilds = false // Or, if you prefer, you can continue to check for errors in release builds, // but continue the build even when errors are found: abortOnError = false // suppress false warning ("Resource IDs will be non-final in Android Gradle Plugin version // 5.0, avoid using them in switch case statements"), which affects only library projects disable += "NonConstantResourceId" } compileOptions { // Flag to enable support for the new language APIs isCoreLibraryDesugaringEnabled = true encoding = "utf-8" } sourceSets { getByName("androidTest") { assets.directories += "$projectDir/schemas" } } androidResources { generateLocaleConfig = true } buildFeatures { viewBinding = true compose = true buildConfig = true resValues = true } packaging { resources { // remove two files which belong to jsoup // no idea how they ended up in the META-INF dir... excludes += setOf( "META-INF/README.md", "META-INF/CHANGES", "META-INF/COPYRIGHT" // "COPYRIGHT" belongs to RxJava... ) } } } ksp { arg("room.schemaLocation", "$projectDir/schemas") } // Custom dependency configuration for ktlint val ktlint by configurations.creating // https://checkstyle.org/#JRE_and_JDK tasks.withType().configureEach { javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(21) } } checkstyle { configDirectory = rootProject.file("checkstyle") isIgnoreFailures = false isShowViolations = true toolVersion = libs.versions.checkstyle.get() } tasks.register("runCheckstyle") { source("src") include("**/*.java") exclude("**/gen/**") exclude("**/R.java") exclude("**/BuildConfig.java") exclude("main/java/us/shandian/giga/**") classpath = configurations.getByName("checkstyle") isShowViolations = true reports { xml.required = true html.required = true } } val outputDir = project.layout.buildDirectory.dir("reports/ktlint/") val inputFiles = fileTree("src") { include("**/*.kt") } tasks.register("runKtlint") { inputs.files(inputFiles) outputs.dir(outputDir) mainClass.set("com.pinterest.ktlint.Main") classpath = configurations.getByName("ktlint") args = listOf("--editorconfig=../.editorconfig", "src/**/*.kt") jvmArgs = listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED") } tasks.register("formatKtlint") { inputs.files(inputFiles) outputs.dir(outputDir) mainClass.set("com.pinterest.ktlint.Main") classpath = configurations.getByName("ktlint") args = listOf("--editorconfig=../.editorconfig", "-F", "src/**/*.kt") jvmArgs = listOf("--add-opens", "java.base/java.lang=ALL-UNNAMED") } tasks.register("checkDependenciesOrder") { tomlFile = layout.projectDirectory.file("../gradle/libs.versions.toml") } afterEvaluate { tasks.named("preDebugBuild").configure { if (!System.getProperties().containsKey("skipFormatKtlint")) { dependsOn("formatKtlint") } dependsOn("runCheckstyle", "runKtlint", "checkDependenciesOrder") } } sonar { properties { property("sonar.projectKey", "TeamNewPipe_NewPipe") property("sonar.organization", "teamnewpipe") property("sonar.host.url", "https://sonarcloud.io") } } aboutLibraries { // note: offline mode prevents the plugin from fetching licenses at build time, which would be // harmful for reproducible builds offlineMode = true duplicationMode = DuplicateMode.MERGE } dependencies { /** Desugaring **/ coreLibraryDesugaring(libs.android.desugar) /** NewPipe libraries **/ implementation(libs.newpipe.nanojson) implementation(libs.newpipe.extractor) implementation(libs.newpipe.filepicker) /** Checkstyle **/ checkstyle(libs.puppycrawl.checkstyle) ktlint(libs.pinterest.ktlint) /** Kotlin **/ implementation(libs.kotlin.stdlib) /** AndroidX **/ implementation(libs.androidx.appcompat) implementation(libs.androidx.cardview) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.core.ktx) implementation(libs.androidx.documentfile) implementation(libs.androidx.fragment.compose) implementation(libs.androidx.lifecycle.livedata) implementation(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.androidx.media) implementation(libs.androidx.preference) implementation(libs.androidx.recyclerview) implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.rxjava3) ksp(libs.androidx.room.compiler) implementation(libs.androidx.swiperefreshlayout) implementation(libs.androidx.work.runtime.ktx) implementation(libs.androidx.work.rxjava3) implementation(libs.google.android.material) implementation(libs.androidx.webkit) /** Compose & other modern patterns **/ // Jetpack Compose implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.adaptive) implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.compose.ui.text) // Needed for parsing HTML to AnnotatedString implementation(libs.androidx.compose.material.icons.extended) // Jetpack Compose related dependencies implementation(libs.androidx.paging.compose) implementation(libs.androidx.navigation.compose) // Coroutines interop implementation(libs.kotlinx.coroutines.rx3) // Library loading for About screen implementation(libs.about.libraries.compose.m3) // Hilt implementation(libs.hilt.android) ksp(libs.hilt.compiler) // Scroll implementation(libs.lazy.column.scrollbar) // Kotlinx Serialization implementation(libs.kotlinx.serialization.json) /** Third-party libraries **/ // Instance state boilerplate elimination implementation(libs.livefront.bridge) implementation(libs.evernote.statesaver.core) kapt(libs.evernote.statesaver.compiler) // HTML parser implementation(libs.jsoup) // HTTP client implementation(libs.squareup.okhttp) // Media player implementation(libs.google.exoplayer.core) implementation(libs.google.exoplayer.dash) implementation(libs.google.exoplayer.database) implementation(libs.google.exoplayer.datasource) implementation(libs.google.exoplayer.hls) implementation(libs.google.exoplayer.mediasession) implementation(libs.google.exoplayer.smoothstreaming) implementation(libs.google.exoplayer.ui) // Manager for complex RecyclerView layouts implementation(libs.lisawray.groupie.core) implementation(libs.lisawray.groupie.viewbinding) // Image loading implementation(libs.coil.compose) implementation(libs.coil.network.okhttp) // Markdown library for Android implementation(libs.noties.markwon.core) implementation(libs.noties.markwon.linkify) // Crash reporting implementation(libs.acra.core) compileOnly(libs.google.autoservice.annotations) ksp(libs.zacsweers.autoservice.compiler) // Properly restarting implementation(libs.jakewharton.phoenix) // Reactive extensions for Java VM implementation(libs.reactivex.rxjava) implementation(libs.reactivex.rxandroid) // RxJava binding APIs for Android UI widgets implementation(libs.jakewharton.rxbinding) // Date and time formatting implementation(libs.ocpsoft.prettytime) /** Debugging **/ // Memory leak detection debugImplementation(libs.squareup.leakcanary.watcher) debugImplementation(libs.squareup.leakcanary.plumber) debugImplementation(libs.squareup.leakcanary.core) // Debug bridge for Android debugImplementation(libs.facebook.stetho.core) debugImplementation(libs.facebook.stetho.okhttp3) // Jetpack Compose debugImplementation(libs.androidx.compose.ui.tooling) /** Testing **/ testImplementation(libs.junit) testImplementation(libs.mockito.core) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.runner) androidTestImplementation(libs.androidx.room.testing) androidTestImplementation(libs.assertj.core) androidTestImplementation(platform(libs.androidx.compose.bom)) androidTestImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.test.manifest) }