Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
plugins {
id("projectConfig")
id("com.android.application")
id("kotlin-android")
id("com.google.devtools.ksp")
Expand All @@ -9,28 +10,32 @@ apply(plugin = "dagger.hilt.android.plugin")
apply(plugin = "androidx.navigation.safeargs.kotlin")

android {
compileSdk = ProjectConfig.compileSdk
namespace = ProjectConfig.packageName
compileSdk = projectConfig.compileSdk

defaultConfig {
applicationId = ProjectConfig.packageName
namespace = projectConfig.packageName

minSdk = ProjectConfig.minSdk
targetSdk = ProjectConfig.targetSdk
minSdk = projectConfig.minSdk
targetSdk = projectConfig.targetSdk

versionCode = ProjectConfig.Version.code + 0 // Base app
versionName = ProjectConfig.Version.name
versionCode = projectConfig.version.code.toInt()
versionName = projectConfig.version.name

testInstrumentationRunner = "eu.darken.capod.HiltTestRunner"

buildConfigField("String", "PACKAGENAME", "\"${projectConfig.packageName}\"")
buildConfigField("String", "VERSION_CODE", "\"${projectConfig.version.code}\"")
buildConfigField("String", "VERSION_NAME", "\"${projectConfig.version.name}\"")
}

// Enable automatic per-app language preferences generation
androidResources {
@Suppress("UnstableApiUsage")
generateLocaleConfig = true
}

signingConfigs {
val basePath = File(System.getProperty("user.home"), ".appconfig/${ProjectConfig.packageName}")
val basePath = File(System.getProperty("user.home"), ".appconfig/${projectConfig.packageName}")
create("releaseFoss") {
setupCredentials(File(basePath, "signing-foss.properties"))
}
Expand Down Expand Up @@ -94,7 +99,7 @@ android {
val variantName: String = variantOutputImpl.name

if (listOf("release", "beta").any { variantName.lowercase().contains(it) }) {
val outputFileName = ProjectConfig.packageName +
val outputFileName = projectConfig.packageName +
"-v${defaultConfig.versionName}-${defaultConfig.versionCode}" +
"-${variantName.uppercase()}.apk"

Expand Down
40 changes: 27 additions & 13 deletions app/src/main/java/eu/darken/capod/common/BuildConfigWrap.kt
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
package eu.darken.capod.common

import eu.darken.capod.BuildConfig
import android.util.Log
import androidx.annotation.Keep
import java.lang.reflect.Field


// Can't be const because that prevents them from being mocked in tests
@Suppress("MayBeConstant")
@Keep
object BuildConfigWrap {
val DEBUG: Boolean = BuildConfig.DEBUG

val BUILD_TYPE: BuildType = when (val typ = BuildConfig.BUILD_TYPE) {
val APPLICATION_ID = getBuildConfigValue("PACKAGENAME") as String
val DEBUG: Boolean = getBuildConfigValue("DEBUG") as Boolean
val BUILD_TYPE: BuildType = when (val typ = getBuildConfigValue("BUILD_TYPE") as String) {
"debug" -> BuildType.DEV
"beta" -> BuildType.BETA
"release" -> BuildType.RELEASE
else -> throw IllegalArgumentException("Unknown buildtype: $typ")
}

@Keep
enum class BuildType {
DEV,
BETA,
RELEASE,
;
}

val FLAVOR: Flavor = when (val flav = BuildConfig.FLAVOR) {
val FLAVOR: Flavor = when (val flav = getBuildConfigValue("FLAVOR") as String?) {
"gplay" -> Flavor.GPLAY
"foss" -> Flavor.FOSS
null -> Flavor.NONE
else -> throw IllegalStateException("Unknown flavor: $flav")
}

enum class Flavor {
GPLAY,
FOSS,
NONE,
;
}

val APPLICATION_ID: String = BuildConfig.APPLICATION_ID

val VERSION_CODE: Long = BuildConfig.VERSION_CODE.toLong()
val VERSION_NAME: String = BuildConfig.VERSION_NAME
val VERSION_CODE: Long = (getBuildConfigValue("VERSION_CODE") as String).toLong()
val VERSION_NAME: String = getBuildConfigValue("VERSION_NAME") as String

val VERSION_DESCRIPTION_LONG: String = "v$VERSION_NAME ($VERSION_CODE) ${FLAVOR}_$BUILD_TYPE"
val VERSION_DESCRIPTION_SHORT: String = "v$VERSION_NAME $FLAVOR"
val VERSION_DESCRIPTION: String = "v$VERSION_NAME ($VERSION_CODE) ~ $FLAVOR/$BUILD_TYPE"
val VERSION_DESCRIPTION_SHORT: String = "v$VERSION_NAME ~ $FLAVOR"
val VERSION_DESCRIPTION_TINY: String = "v$VERSION_NAME"

private fun getBuildConfigValue(fieldName: String): Any? = try {
val c = Class.forName("eu.darken.capod.BuildConfig")
val f: Field = c.getField(fieldName).apply {
isAccessible = true
}
f.get(null)
} catch (e: Exception) {
e.printStackTrace()
Log.e("getBuildConfigValue", "fieldName: $fieldName")
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class RecorderModule @Inject constructor(
triggerFile.createNewFile()

log(TAG, INFO) { "Build.Fingerprint: ${Build.FINGERPRINT}" }
log(TAG, INFO) { "BuildConfig.Versions: ${BuildConfigWrap.VERSION_DESCRIPTION_LONG}" }
log(TAG, INFO) { "BuildConfig.Versions: ${BuildConfigWrap.VERSION_DESCRIPTION}" }

copy(
recorder = newRecorder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class RecorderActivityVM @Inject constructor(
}

fun share() = launch {
val (path, size) = resultCacheCompressedObs.first()
val (path, _) = resultCacheCompressedObs.first()

val intent = Intent(Intent.ACTION_SEND).apply {
val uri = FileProvider.getUriForFile(
Expand All @@ -89,7 +89,7 @@ class RecorderActivityVM @Inject constructor(
type = "application/zip"

addCategory(Intent.CATEGORY_DEFAULT)
putExtra(Intent.EXTRA_SUBJECT, "CAPod DebugLog - ${BuildConfigWrap.VERSION_DESCRIPTION_LONG})")
putExtra(Intent.EXTRA_SUBJECT, "CAPod DebugLog - ${BuildConfigWrap.VERSION_DESCRIPTION})")
putExtra(Intent.EXTRA_TEXT, "Your text here.")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class SettingsIndexFragment : PreferenceFragment2() {
}

override fun onPreferencesCreated() {
findPreference<Preference>("core.changelog")!!.summary = BuildConfigWrap.VERSION_DESCRIPTION_LONG
findPreference<Preference>("core.changelog")!!.summary = BuildConfigWrap.VERSION_DESCRIPTION
findPreference<Preference>("core.privacy")!!.setOnPreferenceClickListener {
webpageTool.open(PrivacyPolicy.URL)
true
Expand Down
10 changes: 10 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ plugins {
`java-library`
}

gradlePlugin {
plugins {
create("projectConfigPlugin") {
id = "projectConfig"
implementationClass = "ProjectConfigPlugin"
}
}
}

repositories {
google()
mavenCentral()
}

dependencies {
implementation("com.android.tools.build:gradle:8.13.0")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.10")
Expand Down
92 changes: 42 additions & 50 deletions buildSrc/src/main/java/ProjectConfig.kt
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.io.FileInputStream
import java.util.Properties

object ProjectConfig {
const val packageName = "eu.darken.capod"
open class ProjectConfig {
val packageName = "eu.darken.capod"
val minSdk = 26

const val minSdk = 26
const val compileSdk = 36
const val targetSdk = 36
val compileSdk = 36
val targetSdk = 36

object Version {
val versionProperties = Properties().apply {
load(FileInputStream(File("version.properties")))
}
val major = versionProperties.getProperty("project.versioning.major").toInt()
val minor = versionProperties.getProperty("project.versioning.minor").toInt()
val patch = versionProperties.getProperty("project.versioning.patch").toInt()
val build = versionProperties.getProperty("project.versioning.build").toInt()
lateinit var version: Version

val name = "${major}.${minor}.${patch}-rc${build}"
val code = major * 10000000 + minor * 100000 + patch * 1000 + build * 10
override fun toString(): String {
return "ProjectConfig($packageName, min=$minSdk, compile=$compileSdk, target=$targetSdk, version=$version)"
}
}

fun lastCommitHash(): String = Runtime.getRuntime().exec("git rev-parse --short HEAD").let { process ->
process.waitFor()
val output = process.inputStream.use { input ->
input.bufferedReader().use {
it.readText()
fun init(project: Project) {
val versionProperties = Properties().apply {
val propsPath = File(project.rootDir, "version.properties")
println("Version: From $propsPath:")
load(FileInputStream(propsPath))
println("$this")
}
version = Version(
major = versionProperties.getProperty("project.versioning.major").toInt(),
minor = versionProperties.getProperty("project.versioning.minor").toInt(),
patch = versionProperties.getProperty("project.versioning.patch").toInt(),
build = versionProperties.getProperty("project.versioning.build").toInt(),
type = versionProperties.getProperty("project.versioning.type"),
)
}
process.destroy()
output.trim()
}

fun com.android.build.api.dsl.SigningConfig.setupCredentials(
signingPropsPath: File? = null
) {

val keyStoreFromEnv = System.getenv("STORE_PATH")?.let { File(it) }

if (keyStoreFromEnv?.exists() == true) {
println("Using signing data from environment variables.")
storeFile = keyStoreFromEnv
storePassword = System.getenv("STORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
} else {
println("Using signing data from properties file.")
val props = Properties().apply {
signingPropsPath?.takeIf { it.canRead() }?.let { load(FileInputStream(it)) }
}

val keyStorePath = props.getProperty("release.storePath")?.let { File(it) }

if (keyStorePath?.exists() == true) {
storeFile = keyStorePath
storePassword = props.getProperty("release.storePassword")
keyAlias = props.getProperty("release.keyAlias")
keyPassword = props.getProperty("release.keyPassword")
}
data class Version(
val major: Int,
val minor: Int,
val patch: Int,
val build: Int,
val type: String,
) {
val name: String
get() = "${major}.${minor}.${patch}-$type${build}"
val code: Long
get() = major * 10000000 + minor * 100000 + patch * 1000 + build * 10L
}
}

class ProjectConfigPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create("projectConfig", ProjectConfig::class.java)
extension.init(project)
project.afterEvaluate { println("ProjectConfigPlugin loaded: $extension") }
}
}
75 changes: 75 additions & 0 deletions buildSrc/src/main/java/ProjectExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import com.android.build.api.dsl.SigningConfig
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.TestDescriptor
import org.gradle.api.tasks.testing.TestListener
import org.gradle.api.tasks.testing.TestResult
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import java.io.File
import java.io.FileInputStream
import java.util.Properties

val Project.projectConfig: ProjectConfig
get() = extensions.findByType(ProjectConfig::class.java)!!

fun SigningConfig.setupCredentials(
signingPropsPath: File? = null
) {

val keyStoreFromEnv = System.getenv("STORE_PATH")?.let { File(it) }

if (keyStoreFromEnv?.exists() == true) {
println("Using signing data from environment variables.")
storeFile = keyStoreFromEnv
storePassword = System.getenv("STORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
} else {
println("Using signing data from properties file.")
val props = Properties().apply {
signingPropsPath?.takeIf { it.canRead() }?.let { load(FileInputStream(it)) }
}

val keyStorePath = props.getProperty("release.storePath")?.let { File(it) }

if (keyStorePath?.exists() == true) {
storeFile = keyStorePath
storePassword = props.getProperty("release.storePassword")
keyAlias = props.getProperty("release.keyAlias")
keyPassword = props.getProperty("release.keyPassword")
}
}
}

fun Test.setupTestLogging() {
testLogging {
events(
TestLogEvent.FAILED,
TestLogEvent.PASSED,
TestLogEvent.SKIPPED,
// TestLogEvent.STANDARD_OUT,
)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true

addTestListener(object : TestListener {
override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {}
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent != null) {
val messages = """
------------------------------------------------------------------------------------------------
| ${result.resultType} ${result.testCount} tests: ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)
------------------------------------------------------------------------------------------------

""".trimIndent()
println(messages)
}
}
})
}
}
2 changes: 0 additions & 2 deletions fastlane/metadata/android/en-US/changelogs/10000.txt

This file was deleted.

4 changes: 0 additions & 4 deletions fastlane/metadata/android/en-US/changelogs/1031300.txt

This file was deleted.

4 changes: 0 additions & 4 deletions fastlane/metadata/android/en-US/changelogs/1031301.txt

This file was deleted.

4 changes: 0 additions & 4 deletions fastlane/metadata/android/en-US/changelogs/1031302.txt

This file was deleted.

4 changes: 0 additions & 4 deletions fastlane/metadata/android/en-US/changelogs/1031303.txt

This file was deleted.

Loading