Files
Gearslip/gearslip/build.gradle.kts
T
Seb3thehacker 5f9701ccff Release 0.0.05-alpha: guided setup, downloadable certificate, car phone app, media rework
- First run now asks for permissions one at a time, each on its own screen with an Allow or
  Skip, instead of firing every system dialog at once from a cold start
- The head unit certificate can be downloaded straight from the opencardev/aasdk repository
  with one tap, on top of the existing adb-staged and file-import paths; Settings shows which
  source is active and lets an imported or downloaded certificate be removed
- A phone app on the car screen: a keypad and a searchable contacts list, with a confirmation
  card before every call
- The web browser draws its own on-screen keyboard for a page's text fields, so a search box or
  address bar can be typed into from the car screen
- Media player rework: a dedicated Now Playing tab with a bigger, screen-size-aware layout, an
  Up Next tab showing the app's real queue, and Browse/Lyrics tabs that share the same rail
- Waze and Google Messages are marked broken instead of pretending to work; Google's own dialer,
  Assistant, and Android Auto entries are hidden from the car launcher entirely; Aardvark and
  Vivi Music are marked tested and working
- A help screen replaces the old wall of setup text on the home screen
- The display-calibration screen is gone - it did its job
2026-09-26 16:53:28 -07:00

115 lines
4.5 KiB
Kotlin

import java.util.Properties
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
/**
* Gearslip: a phone-side Android Auto client built against the reverse-engineered AA wire
* protocol, so a non-Google app can authenticate to and project onto a real car head unit
* with no gearhead in the path. No third-party dependencies: the X.509 certificate is
* generated by hand (see `SelfSignedCert`) so there is no BouncyCastle version to track and
* no second crypto provider to collide with Conscrypt.
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
// The release signing key, read from an untracked keystore.properties beside this file or at the
// repository root. Third-party apps allowlist Gearslip by the SHA-256 of this certificate, so the
// key must stay the same for the life of the project - and must never be committed.
val keystoreProperties = Properties().apply {
listOf(
rootProject.file("keystore.properties"),
project.file("keystore.properties"),
).firstOrNull { it.exists() }?.inputStream()?.use { load(it) }
}
android {
namespace = "app.seb3thehacker.gearslip"
compileSdk = 37
defaultConfig {
applicationId = "app.seb3thehacker.gearslip"
minSdk = 31
targetSdk = 37
versionCode = 5
versionName = "0.0.05-alpha"
}
signingConfigs {
// Declared only when the properties file is present, so a clone without the key still
// builds debug; assembleRelease then falls back to unsigned rather than failing here.
if (keystoreProperties.getProperty("storeFile") != null) {
create("release") {
storeFile = rootProject.file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
}
}
}
buildTypes {
release {
signingConfig = signingConfigs.findByName("release")
}
}
// Keeps build output out of the module folder: <root>/build/gearslip/... instead of
// <root>/gearslip/build/..., so everything this project produces lands under one
// root-level build/ directory.
layout.buildDirectory.set(rootProject.layout.buildDirectory.dir("gearslip"))
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
testOptions {
// GearslipLog calls android.util.Log, which is a stub on the local JVM.
unitTests.isReturnDefaultValues = true
}
}
// Renames the produced APK and moves it to the root build/ folder (<root>/build/Gearslip-debug.apk)
// after the fact rather than fighting AGP's variant-output API across versions - simpler and
// doesn't break when that API moves again.
tasks.register("renameApk") {
doLast {
val rootBuildDir = rootProject.layout.buildDirectory.get().asFile
layout.buildDirectory.dir("outputs/apk").get().asFile.walkTopDown()
.filter { it.extension == "apk" }
.toList()
.forEach { apk ->
val renamed = File(rootBuildDir, "Gearslip-${apk.parentFile.name}.apk")
apk.copyTo(renamed, overwrite = true)
apk.delete()
}
}
}
tasks.matching { it.name.startsWith("assemble") }.configureEach { finalizedBy("renameApk") }
dependencies {
// Only for the XML window theme (Theme.Material3.DynamicColors); every screen is Compose.
implementation(libs.google.material)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material.icons.core)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
// Used from the host side, not the app side: Gearslip renders other apps' templates rather
// than publishing its own, so this is here for the AIDL interfaces and the model classes
// their Bundleables deserialize into.
implementation(libs.androidx.car.app)
implementation(libs.androidx.car.app.projected)
testImplementation(libs.junit)
}
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}