mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
## Description Adds a setting to configure the app's language/locale. It is configured per user. This settings has no impact on audio or subtitles language choices. Only languages Wholphin has translations for can be used. If you would like to contribute new translations, check out https://translate.codeberg.org/projects/wholphin/! Dev notes: This doesn't exactly follow Android's recommendations because the [guidelines are for per-app](https://developer.android.com/guide/topics/resources/app-languages) and not users within the app. ### Related issues Closes #513 Closes #669 ### Testing - Emulator API 34 - Nvidia shield API 30 - Fire TV OS 7 API 28 ## Screenshots <img width="1280" height="720" alt="language Large" src="https://github.com/user-attachments/assets/bf1c0962-8a36-4012-aed9-eab564fc1114" /> ## AI or LLM usage None
392 lines
14 KiB
Kotlin
392 lines
14 KiB
Kotlin
import com.android.build.api.dsl.ApplicationExtension
|
|
import com.android.build.api.dsl.ProductFlavor
|
|
import com.android.build.api.variant.FilterConfiguration
|
|
import com.google.protobuf.gradle.id
|
|
import com.mikepenz.aboutlibraries.plugin.DuplicateMode
|
|
import com.mikepenz.aboutlibraries.plugin.DuplicateRule
|
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
import java.util.Base64
|
|
import java.util.Properties
|
|
|
|
plugins {
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.ksp)
|
|
alias(libs.plugins.kotlin.compose)
|
|
alias(libs.plugins.hilt)
|
|
alias(libs.plugins.room)
|
|
alias(libs.plugins.protobuf)
|
|
alias(libs.plugins.kotlin.plugin.serialization)
|
|
alias(libs.plugins.aboutLibraries)
|
|
alias(libs.plugins.openapi.generator)
|
|
}
|
|
|
|
val isCI = if (System.getenv("CI") != null) System.getenv("CI").toBoolean() else false
|
|
val shouldSign = isCI && System.getenv("KEY_ALIAS") != null
|
|
val ffmpegModuleExists = project.file("libs/lib-decoder-ffmpeg-release.aar").exists()
|
|
val av1ModuleExists = project.file("libs/lib-decoder-av1-release.aar").exists()
|
|
val mpvModuleExists = project.file("libs/wholphin-mpv-release.aar").exists()
|
|
val extensionsRepoActive = project.hasProperty("WholphinExtensionsUsername")
|
|
|
|
val gitTags =
|
|
providers
|
|
.exec { commandLine("git", "tag", "--list", "v*", "p*") }
|
|
.standardOutput.asText
|
|
.get()
|
|
|
|
val gitDescribe =
|
|
providers
|
|
.exec { commandLine("git", "describe", "--tags", "--long", "--match=v*") }
|
|
.standardOutput.asText
|
|
.getOrElse("v0.0.0")
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3
|
|
jvmTarget = JvmTarget.JVM_11
|
|
javaParameters = true
|
|
}
|
|
}
|
|
|
|
configure<ApplicationExtension> {
|
|
namespace = "com.github.damontecres.wholphin"
|
|
compileSdk = 36
|
|
|
|
defaultConfig {
|
|
applicationId = "com.github.damontecres.wholphin"
|
|
minSdk = 23
|
|
targetSdk = 36
|
|
versionCode = gitTags.trim().lines().size
|
|
versionName = gitDescribe.trim().removePrefix("v").ifBlank { "0.0.0" }
|
|
testInstrumentationRunner = "com.github.damontecres.wholphin.test.WholphinTestRunner"
|
|
|
|
buildConfigField("long", "BUILD_TIME", System.currentTimeMillis().toString())
|
|
}
|
|
|
|
signingConfigs {
|
|
if (shouldSign) {
|
|
create("ci") {
|
|
file("ci.keystore").writeBytes(
|
|
Base64.getDecoder().decode(System.getenv("SIGNING_KEY")),
|
|
)
|
|
keyAlias = System.getenv("KEY_ALIAS")
|
|
keyPassword = System.getenv("KEY_PASSWORD")
|
|
storePassword = System.getenv("KEY_STORE_PASSWORD")
|
|
storeFile = file("ci.keystore")
|
|
enableV1Signing = true
|
|
enableV2Signing = true
|
|
enableV3Signing = true
|
|
enableV4Signing = true
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro",
|
|
)
|
|
isDebuggable = false
|
|
if (shouldSign) {
|
|
signingConfig = signingConfigs.getByName("ci")
|
|
} else {
|
|
val localPropertiesFile = project.rootProject.file("local.properties")
|
|
if (localPropertiesFile.exists()) {
|
|
val properties = Properties()
|
|
properties.load(localPropertiesFile.inputStream())
|
|
val signingConfigName = properties["release.signing.config"]?.toString()
|
|
if (signingConfigName != null) {
|
|
signingConfig = signingConfigs.getByName(signingConfigName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
debug {
|
|
isMinifyEnabled = false
|
|
isShrinkResources = false
|
|
isDebuggable = true
|
|
applicationIdSuffix = ".debug"
|
|
}
|
|
}
|
|
flavorDimensions += "version"
|
|
productFlavors {
|
|
val featureLeanback = "leanback"
|
|
val featureUpdate = "UPDATING_ENABLED"
|
|
val featureDiscover = "DISCOVER_ENABLED"
|
|
|
|
fun ProductFlavor.setFeatureFlag(
|
|
name: String,
|
|
enabled: Boolean,
|
|
) {
|
|
this.buildConfigField("boolean", name, "Boolean.parseBoolean(\"${enabled}\")")
|
|
}
|
|
create("default") {
|
|
dimension = "version"
|
|
isDefault = true
|
|
manifestPlaceholders += mapOf(featureLeanback to false)
|
|
setFeatureFlag(featureUpdate, true)
|
|
setFeatureFlag(featureDiscover, true)
|
|
}
|
|
create("appstore") {
|
|
dimension = "version"
|
|
manifestPlaceholders += mapOf(featureLeanback to true)
|
|
setFeatureFlag(featureUpdate, false)
|
|
setFeatureFlag(featureDiscover, true)
|
|
}
|
|
create("firetv") {
|
|
dimension = "version"
|
|
manifestPlaceholders += mapOf(featureLeanback to true)
|
|
setFeatureFlag(featureUpdate, false)
|
|
setFeatureFlag(featureDiscover, false)
|
|
}
|
|
}
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_11
|
|
targetCompatibility = JavaVersion.VERSION_11
|
|
isCoreLibraryDesugaringEnabled = true
|
|
}
|
|
|
|
buildFeatures {
|
|
buildConfig = true
|
|
compose = true
|
|
}
|
|
|
|
splits {
|
|
abi {
|
|
isEnable = true
|
|
reset()
|
|
include("armeabi-v7a", "arm64-v8a")
|
|
isUniversalApk = true
|
|
}
|
|
}
|
|
packaging {
|
|
jniLibs {
|
|
// Work around because libass-android & wholphin-mpv both (incorrectly) package libc++_shared.so
|
|
pickFirsts += "lib/*/libc++_shared.so"
|
|
}
|
|
}
|
|
|
|
sourceSets {
|
|
getByName("main") {
|
|
kotlin.directories += "$buildDir/generated/seerr_api/src/main/kotlin"
|
|
}
|
|
}
|
|
|
|
testOptions {
|
|
unitTests {
|
|
isIncludeAndroidResources = true
|
|
}
|
|
}
|
|
|
|
lint {
|
|
disable.add("MissingTranslation")
|
|
}
|
|
androidResources {
|
|
generateLocaleConfig = true
|
|
}
|
|
}
|
|
|
|
androidComponents {
|
|
onVariants(selector().all()) { variant ->
|
|
variant.outputs
|
|
.map { it as com.android.build.api.variant.impl.VariantOutputImpl }
|
|
.forEach { output ->
|
|
val abi =
|
|
output
|
|
.getFilter(FilterConfiguration.FilterType.ABI)
|
|
.let { if (it != null) "-${it.identifier}" else "" }
|
|
val outputFileName =
|
|
"Wholphin-${variant.flavorName}-${variant.buildType}-${output.versionName.get()}-${output.versionCode.get()}$abi.apk"
|
|
output.outputFileName = outputFileName
|
|
}
|
|
}
|
|
}
|
|
|
|
room {
|
|
schemaDirectory("$projectDir/schemas")
|
|
}
|
|
|
|
protobuf {
|
|
protoc {
|
|
artifact = "com.google.protobuf:protoc:${libs.protobuf.kotlin.lite.get().version}"
|
|
}
|
|
generateProtoTasks {
|
|
all().forEach {
|
|
it.plugins {
|
|
id("java") {
|
|
option("lite")
|
|
}
|
|
}
|
|
it.builtins {
|
|
id("kotlin") {
|
|
option("lite")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
aboutLibraries {
|
|
collect {
|
|
configPath = file("config")
|
|
}
|
|
library {
|
|
duplicationMode = DuplicateMode.MERGE
|
|
duplicationRule = DuplicateRule.SIMPLE
|
|
}
|
|
}
|
|
|
|
openApiGenerate {
|
|
generatorName.set("kotlin")
|
|
inputSpec.set("$projectDir/src/main/seerr/seerr-api.yml")
|
|
templateDir.set("$projectDir/src/main/seerr/templates")
|
|
outputDir.set("$buildDir/generated/seerr_api")
|
|
apiPackage.set("com.github.damontecres.wholphin.api.seerr")
|
|
modelPackage.set("com.github.damontecres.wholphin.api.seerr.model")
|
|
groupId.set("com.github.damontecres.wholphin.api.seerr")
|
|
id.set("seerr-api")
|
|
packageName.set("com.github.damontecres.wholphin.api.seerr")
|
|
additionalProperties.apply {
|
|
put("serializationLibrary", "kotlinx_serialization")
|
|
put("sortModelPropertiesByRequiredFlag", true)
|
|
put("sortParamsByRequiredFlag", true)
|
|
put("useCoroutines", true)
|
|
put("enumPropertyNaming", "UPPERCASE")
|
|
put("modelMutable", false)
|
|
|
|
// Note: this is only for downloading files, so it's not necessary to enable
|
|
put("supportAndroidApiLevel25AndBelow", false)
|
|
}
|
|
}
|
|
|
|
tasks.named("preBuild") {
|
|
dependsOn.add(tasks.named("openApiGenerate"))
|
|
}
|
|
|
|
dependencies {
|
|
implementation(libs.androidx.core.ktx)
|
|
implementation(libs.androidx.appcompat)
|
|
implementation(platform(libs.androidx.compose.bom))
|
|
implementation(libs.androidx.compose.ui)
|
|
implementation(libs.androidx.compose.ui.graphics)
|
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
|
implementation(libs.androidx.compose.runtime)
|
|
implementation(libs.androidx.compose.runtime.livedata)
|
|
implementation(libs.androidx.tv.foundation)
|
|
implementation(libs.androidx.tv.material)
|
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
implementation(libs.androidx.lifecycle.livedata.ktx)
|
|
implementation(libs.androidx.activity.compose)
|
|
implementation(libs.androidx.datastore)
|
|
implementation(libs.androidx.datastore.preferences)
|
|
implementation(libs.protobuf.kotlin.lite)
|
|
implementation(libs.androidx.tvprovider)
|
|
implementation(libs.androidx.work.runtime.ktx)
|
|
implementation(libs.androidx.hilt.work)
|
|
|
|
implementation(libs.androidx.media3.exoplayer)
|
|
implementation(libs.androidx.media3.session)
|
|
implementation(libs.androidx.media3.datasource.okhttp)
|
|
implementation(libs.androidx.media3.exoplayer.hls)
|
|
implementation(libs.androidx.media3.exoplayer.dash)
|
|
implementation(libs.androidx.media3.ui)
|
|
implementation(libs.androidx.media3.ui.compose)
|
|
implementation(libs.ass.media)
|
|
|
|
implementation(libs.coil.core)
|
|
implementation(libs.coil.compose)
|
|
implementation(libs.coil.network.cachecontrol)
|
|
implementation(libs.coil.network.okhttp)
|
|
implementation(libs.coil.gif)
|
|
implementation(libs.coil.svg)
|
|
|
|
implementation(libs.jellyfin.core)
|
|
implementation(libs.jellyfin.api)
|
|
implementation(libs.jellyfin.api.okhttp)
|
|
|
|
implementation(libs.androidx.navigation3.ui)
|
|
implementation(libs.androidx.navigation3.runtime)
|
|
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
|
implementation(libs.androidx.material3.adaptive.navigation3)
|
|
implementation(libs.kotlinx.serialization.core)
|
|
implementation(libs.kotlinx.serialization.json)
|
|
|
|
implementation(libs.hilt.android)
|
|
implementation(libs.androidx.room.common.jvm)
|
|
implementation(libs.androidx.room.ktx)
|
|
implementation(libs.androidx.compose.material3)
|
|
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
|
implementation(libs.androidx.hilt.navigation.compose)
|
|
implementation(libs.androidx.preference.ktx)
|
|
implementation(libs.androidx.room.testing)
|
|
implementation(libs.androidx.palette.ktx)
|
|
implementation(libs.androidx.media3.effect)
|
|
implementation(libs.androidx.runner)
|
|
ksp(libs.androidx.room.compiler)
|
|
ksp(libs.hilt.android.compiler)
|
|
ksp(libs.androidx.hilt.compiler)
|
|
|
|
implementation(libs.timber)
|
|
implementation(libs.slf4j2.timber)
|
|
implementation(libs.aboutlibraries.core)
|
|
implementation(libs.aboutlibraries.compose.m3)
|
|
implementation(libs.multiplatform.markdown.renderer)
|
|
implementation(libs.multiplatform.markdown.renderer.m3)
|
|
implementation(libs.programguide)
|
|
implementation(libs.acra.http)
|
|
implementation(libs.acra.dialog)
|
|
implementation(libs.acra.limiter)
|
|
compileOnly(libs.auto.service.annotations)
|
|
ksp(libs.auto.service.ksp)
|
|
implementation(platform(libs.okhttp.bom))
|
|
implementation(libs.okhttp)
|
|
implementation(libs.kache)
|
|
implementation(libs.kache.file)
|
|
|
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
|
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
|
|
|
if (ffmpegModuleExists) {
|
|
logger.info("Using local ffmpeg decoder")
|
|
implementation(files("libs/lib-decoder-ffmpeg-release.aar"))
|
|
} else if (extensionsRepoActive) {
|
|
logger.info("Using prebuilt ffmpeg decoder")
|
|
implementation(libs.wholphin.extensions.ffmpeg)
|
|
} else {
|
|
logger.warn("Media3 ffmpeg decoder was NOT found")
|
|
}
|
|
if (av1ModuleExists) {
|
|
logger.info("Using local av1 decoder")
|
|
implementation(files("libs/lib-decoder-av1-release.aar"))
|
|
} else if (extensionsRepoActive) {
|
|
logger.info("Using prebuilt av1 decoder")
|
|
implementation(libs.wholphin.extensions.av1)
|
|
} else {
|
|
logger.warn("Media3 av1 decoder was NOT found")
|
|
}
|
|
if (mpvModuleExists) {
|
|
logger.info("Using local libMPV build")
|
|
implementation(files("libs/wholphin-mpv-release.aar"))
|
|
} else if (extensionsRepoActive) {
|
|
logger.info("Using prebuilt libMPV")
|
|
implementation(libs.wholphin.extensions.mpv)
|
|
} else {
|
|
logger.warn("libMPV was NOT found")
|
|
}
|
|
|
|
testImplementation(libs.mockk.android)
|
|
testImplementation(libs.mockk.agent)
|
|
testImplementation(libs.kotlinx.coroutines.test)
|
|
testImplementation(libs.androidx.core.testing)
|
|
testImplementation(libs.robolectric)
|
|
testImplementation(libs.hilt.android.testing)
|
|
testImplementation(libs.androidx.compose.ui.test.junit4)
|
|
androidTestImplementation(libs.mockk.android)
|
|
androidTestImplementation(libs.hilt.android.testing)
|
|
androidTestImplementation(libs.androidx.ui.test.manifest)
|
|
}
|