import com.android.tools.profgen.ArtProfileKt import com.android.tools.profgen.ArtProfileSerializer import com.android.tools.profgen.DexFile import com.mikepenz.aboutlibraries.plugin.DuplicateMode plugins { alias libs.plugins.android.application alias libs.plugins.kotlin.android alias libs.plugins.kotlin.compose alias libs.plugins.kotlin.kapt alias libs.plugins.kotlin.parcelize alias libs.plugins.checkstyle alias libs.plugins.sonarqube alias libs.plugins.hilt alias libs.plugins.aboutlibraries } android { compileSdk 34 namespace 'org.schabi.newpipe' defaultConfig { applicationId "org.schabi.newpipe" resValue "string", "app_name", "NewPipe" minSdk 21 targetSdk 33 versionCode 999 versionName "0.27.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] } } } buildTypes { debug { debuggable true // suffix the app id and the app name with git branch name def workingBranch = getGitWorkingBranch() def normalizedWorkingBranch = workingBranch.replaceFirst("^[^A-Za-z]+", "").replaceAll("[^0-9A-Za-z]+", "") if (normalizedWorkingBranch.isEmpty() || workingBranch == "master" || workingBranch == "dev") { // 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 archivesBaseName = 'NewPipe_' + normalizedWorkingBranch } } release { if (System.properties.containsKey('packageSuffix')) { applicationIdSuffix System.getProperty('packageSuffix') resValue "string", "app_name", "NewPipe " + System.getProperty('packageSuffix') archivesBaseName = 'NewPipe_' + System.getProperty('packageSuffix') } minifyEnabled true shrinkResources false // disabled to fix F-Droid's reproducible build proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' archivesBaseName = 'app' } } 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 coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 encoding 'utf-8' } kotlinOptions { jvmTarget = JavaVersion.VERSION_17 } sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } buildFeatures { viewBinding true compose true buildConfig true } packagingOptions { resources { // remove two files which belong to jsoup // no idea how they ended up in the META-INF dir... excludes += ['META-INF/README.md', 'META-INF/CHANGES', // 'COPYRIGHT' belongs to RxJava... 'META-INF/COPYRIGHT'] } } } configurations { checkstyle ktlint } checkstyle { getConfigDirectory().set(rootProject.file("checkstyle")) ignoreFailures false showViolations true toolVersion = libs.versions.checkstyle.get() } tasks.register('runCheckstyle', Checkstyle) { source 'src' include '**/*.java' exclude '**/gen/**' exclude '**/R.java' exclude '**/BuildConfig.java' exclude 'main/java/us/shandian/giga/**' classpath = configurations.checkstyle showViolations true reports { xml.getRequired().set(true) html.getRequired().set(true) } } def outputDir = "${project.buildDir}/reports/ktlint/" def inputFiles = project.fileTree(dir: "src", include: "**/*.kt") tasks.register('runKtlint', JavaExec) { inputs.files(inputFiles) outputs.dir(outputDir) getMainClass().set("com.pinterest.ktlint.Main") classpath = configurations.ktlint args "src/**/*.kt" jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } tasks.register('formatKtlint', JavaExec) { inputs.files(inputFiles) outputs.dir(outputDir) getMainClass().set("com.pinterest.ktlint.Main") classpath = configurations.ktlint args "-F", "src/**/*.kt" jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } apply from: 'check-dependencies.gradle' afterEvaluate { if (!System.properties.containsKey('skipFormatKtlint')) { preDebugBuild.dependsOn formatKtlint } preDebugBuild.dependsOn runCheckstyle, runKtlint, checkDependenciesOrder } sonar { properties { property "sonar.projectKey", "TeamNewPipe_NewPipe" property "sonar.organization", "teamnewpipe" property "sonar.host.url", "https://sonarcloud.io" } } kapt { correctErrorTypes true } 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.desugar.jdk.libs.nio /** NewPipe libraries **/ implementation libs.teamnewpipe.nanojson implementation libs.teamnewpipe.newpipe.extractor implementation libs.teamnewpipe.nononsense.filepicker /** Checkstyle **/ checkstyle libs.tools.checkstyle ktlint libs.tools.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 implementation libs.androidx.localbroadcastmanager implementation libs.androidx.media implementation libs.androidx.preference implementation libs.androidx.recyclerview implementation libs.androidx.room.runtime implementation libs.androidx.room.rxjava3 kapt libs.androidx.room.compiler implementation libs.androidx.swiperefreshlayout implementation libs.androidx.work.runtime implementation libs.androidx.work.rxjava3 implementation libs.androidx.material /** Third-party libraries **/ // Instance state boilerplate elimination implementation libs.livefront.bridge implementation libs.android.state kapt libs.android.state.processor // HTML parser implementation libs.jsoup // HTTP client implementation libs.okhttp // Media player implementation libs.exoplayer.core implementation libs.exoplayer.dash implementation libs.exoplayer.database implementation libs.exoplayer.datasource implementation libs.exoplayer.hls implementation libs.exoplayer.smoothstreaming implementation libs.exoplayer.ui implementation libs.extension.mediasession // Metadata generator for service descriptors compileOnly libs.auto.service kapt libs.auto.service.kapt // Manager for complex RecyclerView layouts implementation libs.lisawray.groupie implementation libs.lisawray.groupie.viewbinding // Image loading implementation libs.coil.compose implementation libs.coil.network.okhttp // Markdown library for Android implementation libs.markwon.core implementation libs.markwon.linkify // Crash reporting implementation libs.acra.core // Properly restarting implementation libs.process.phoenix // Reactive extensions for Java VM implementation libs.rxjava3.rxjava implementation libs.rxjava3.rxandroid // RxJava binding APIs for Android UI widgets implementation libs.rxbinding4.rxbinding // Date and time formatting implementation libs.prettytime // 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.aboutlibraries.compose.m3 // Hilt implementation libs.hilt.android kapt(libs.hilt.compiler) // Scroll implementation libs.lazycolumnscrollbar /** Debugging **/ // Memory leak detection debugImplementation libs.leakcanary.object.watcher debugImplementation libs.leakcanary.plumber.android debugImplementation libs.leakcanary.android.core // Debug bridge for Android debugImplementation libs.stetho debugImplementation libs.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 } static String getGitWorkingBranch() { try { def gitProcess = "git rev-parse --abbrev-ref HEAD".execute() gitProcess.waitFor() if (gitProcess.exitValue() == 0) { return gitProcess.text.trim() } else { // not a git repository return "" } } catch (IOException ignored) { // git was not found return "" } } // fix reproducible builds project.afterEvaluate { tasks.compileReleaseArtProfile.doLast { outputs.files.each { file -> if (file.toString().endsWith(".profm")) { println("Sorting ${file} ...") def version = ArtProfileSerializer.valueOf("METADATA_0_0_2") def profile = ArtProfileKt.ArtProfile(file) def keys = new ArrayList(profile.profileData.keySet()) def sortedData = new LinkedHashMap() Collections.sort keys, new DexFile.Companion() keys.each { key -> sortedData[key] = profile.profileData[key] } new FileOutputStream(file).with { write(version.magicBytes$profgen) write(version.versionBytes$profgen) version.write$profgen(it, sortedData, "") } } } } }