init: android project scaffold (Phase 28.1)
- Kotlin DSL build with version catalog (AGP 8.7, Kotlin 2.1, Compose BOM 2025.01) - app module: Compose + Material 3, dark theme, network security config - DualNetworkTestScreen — Phase 28.1 gate screen (buttons wired in 28.1.2) - YarisApp, MainActivity, YarisOBDTheme - ProGuard rules for Retrofit/OkHttp/Kotlinx Serialization/Coil/MapLibre/Room - cleartext allowed for 10.42.0.1 + yaris.local - gradle wrapper properties (needs Android Studio to generate wrapper jar)
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
# Android project ignores
|
||||||
|
*.iml
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
local.properties
|
||||||
|
.idea/
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!gradle/wrapper/gradle-wrapper.properties
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
alias(libs.plugins.kotlin.serialization)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.yarisobd.app"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.yarisobd.app"
|
||||||
|
minSdk = 26
|
||||||
|
targetSdk = 35
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "0.1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
versionNameSuffix = "-debug"
|
||||||
|
}
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "17"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Compose BOM
|
||||||
|
val composeBom = platform(libs.compose.bom)
|
||||||
|
implementation(composeBom)
|
||||||
|
androidTestImplementation(composeBom)
|
||||||
|
|
||||||
|
// Compose UI
|
||||||
|
implementation(libs.compose.ui)
|
||||||
|
implementation(libs.compose.ui.graphics)
|
||||||
|
implementation(libs.compose.ui.tooling.preview)
|
||||||
|
implementation(libs.compose.material3)
|
||||||
|
implementation(libs.compose.material.icons)
|
||||||
|
debugImplementation(libs.compose.ui.tooling)
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
implementation(libs.navigation.compose)
|
||||||
|
|
||||||
|
// Activity + Compose integration
|
||||||
|
implementation(libs.activity.compose)
|
||||||
|
|
||||||
|
// Core
|
||||||
|
implementation(libs.core.ktx)
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
implementation(libs.lifecycle.runtime.compose)
|
||||||
|
implementation(libs.lifecycle.viewmodel.compose)
|
||||||
|
|
||||||
|
// Network — will be used in 28.1.2
|
||||||
|
// implementation(libs.okhttp)
|
||||||
|
// implementation(libs.okhttp.logging)
|
||||||
|
// implementation(libs.retrofit)
|
||||||
|
// implementation(libs.retrofit.kotlinx.serialization)
|
||||||
|
// implementation(libs.kotlinx.serialization.json)
|
||||||
|
|
||||||
|
// Coroutines
|
||||||
|
implementation(libs.kotlinx.coroutines.android)
|
||||||
|
|
||||||
|
// Local storage — will be used in 28.3
|
||||||
|
// implementation(libs.room.runtime)
|
||||||
|
// implementation(libs.room.ktx)
|
||||||
|
// ksp(libs.room.compiler)
|
||||||
|
|
||||||
|
// Background work — will be used in 28.3
|
||||||
|
// implementation(libs.workmanager)
|
||||||
|
|
||||||
|
// Images — will be used in 28.2
|
||||||
|
// implementation(libs.coil.compose)
|
||||||
|
// implementation(libs.coil.okhttp)
|
||||||
|
|
||||||
|
// Maps — will be used in 28.4
|
||||||
|
// implementation(libs.maplibre)
|
||||||
|
|
||||||
|
// Security — will be used in 28.1.3
|
||||||
|
// implementation(libs.security.crypto)
|
||||||
|
|
||||||
|
// Testing
|
||||||
|
testImplementation("junit:junit:4.13.2")
|
||||||
|
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
||||||
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
||||||
|
androidTestImplementation(composeBom)
|
||||||
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||||
|
}
|
||||||
Vendored
+45
@@ -0,0 +1,45 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# Keep rules for libraries used:
|
||||||
|
|
||||||
|
# Retrofit
|
||||||
|
-keepattributes Signature, InnerClasses, EnclosingMethod
|
||||||
|
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
|
||||||
|
-keepclassmembers,allowshrinking,allowobfuscation interface * {
|
||||||
|
@retrofit2.http.* <methods>;
|
||||||
|
}
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
-dontwarn kotlin.Unit
|
||||||
|
-dontwarn retrofit2.KotlinExtensions
|
||||||
|
-dontwarn retrofit2.KotlinExtensions$*
|
||||||
|
|
||||||
|
# OkHttp
|
||||||
|
-dontwarn okhttp3.**
|
||||||
|
-dontwarn okio.**
|
||||||
|
|
||||||
|
# Kotlinx Serialization
|
||||||
|
-keepattributes *Annotation*, InnerClasses
|
||||||
|
-dontnote kotlinx.serialization.AnnotationsKt
|
||||||
|
-keepclassmembers class kotlinx.serialization.json.** {
|
||||||
|
*** Companion;
|
||||||
|
}
|
||||||
|
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
-keep,includedescriptorclasses class com.yarisobd.app.**$$serializer { *; }
|
||||||
|
-keepclassmembers class com.yarisobd.app.** {
|
||||||
|
*** Companion;
|
||||||
|
}
|
||||||
|
-keepclasseswithmembers class com.yarisobd.app.** {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Coil
|
||||||
|
-dontwarn coil.**
|
||||||
|
|
||||||
|
# MapLibre
|
||||||
|
-keep class org.maplibre.** { *; }
|
||||||
|
|
||||||
|
# Room
|
||||||
|
-keep class * extends androidx.room.RoomDatabase
|
||||||
|
-dontwarn androidx.room.paging.**
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Internet: Pi hotspot API calls + remote server uploads -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
<!-- Foreground service for uploads (28.3) -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
|
<!-- Notification permission for upload progress (Android 13+) -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
<!-- Access external storage for downloaded dashcam files -->
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".YarisApp"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:fullBackupOnly="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.YarisOBD"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@style/Theme.YarisOBD">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.yarisobd.app
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phase 28.1 gate screen — proves dual-network binding works on the real device
|
||||||
|
* before building anything else.
|
||||||
|
*
|
||||||
|
* Two buttons:
|
||||||
|
* "Test Pi" → calls 10.42.0.1/api/gps/status over WiFi
|
||||||
|
* "Test Server" → calls yaris.ceaf.dev/health over cellular
|
||||||
|
*
|
||||||
|
* Both must succeed simultaneously while the phone is associated with the
|
||||||
|
* Pi's no-internet hotspot ("yaris 911").
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun DualNetworkTestScreen() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(24.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "yaris-obd",
|
||||||
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Phase 28.1 — Network Binding Proof",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
|
// Pi WiFi test
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Pi Hotspot (WiFi)",
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Target: 10.42.0.1/api/gps/status",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
Button(
|
||||||
|
onClick = { /* TODO: 28.1.2 — WiFi-bound OkHttp call */ },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Test Pi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// Server cellular test
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Remote Server (Cellular)",
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Target: yaris.ceaf.dev/health",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
Button(
|
||||||
|
onClick = { /* TODO: 28.1.2 — Cellular-bound OkHttp call */ },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("Test Server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Connect phone to \"yaris 911\" hotspot first.",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.yarisobd.app
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.yarisobd.app.ui.theme.YarisOBDTheme
|
||||||
|
|
||||||
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
YarisOBDTheme {
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
// Placeholder — replaced by nav host in 28.1.2
|
||||||
|
DualNetworkTestScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.yarisobd.app
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
|
||||||
|
class YarisApp : Application() {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
instance = this
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
lateinit var instance: YarisApp
|
||||||
|
private set
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.yarisobd.app.ui.theme
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
|
||||||
|
// Brand colors — dark, car-friendly. Inspired by the Pi web UI's dark sidebar.
|
||||||
|
private val YarisGreen = Color(0xFF4CAF50)
|
||||||
|
private val YarisGreenDark = Color(0xFF388E3C)
|
||||||
|
private val YarisBackground = Color(0xFF121212)
|
||||||
|
private val YarisSurface = Color(0xFF1E1E1E)
|
||||||
|
private val YarisSurfaceVariant = Color(0xFF2C2C2C)
|
||||||
|
|
||||||
|
private val DarkColorScheme = darkColorScheme(
|
||||||
|
primary = YarisGreen,
|
||||||
|
onPrimary = Color.Black,
|
||||||
|
primaryContainer = YarisGreenDark,
|
||||||
|
secondary = Color(0xFF81C784),
|
||||||
|
background = YarisBackground,
|
||||||
|
surface = YarisSurface,
|
||||||
|
surfaceContainerHigh = YarisSurfaceVariant,
|
||||||
|
onBackground = Color(0xFFE0E0E0),
|
||||||
|
onSurface = Color(0xFFE0E0E0),
|
||||||
|
onSurfaceVariant = Color(0xFFAAAAAA),
|
||||||
|
error = Color(0xFFEF5350),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Light scheme exists for completeness but the app defaults to dark
|
||||||
|
private val LightColorScheme = lightColorScheme(
|
||||||
|
primary = YarisGreenDark,
|
||||||
|
onPrimary = Color.White,
|
||||||
|
primaryContainer = Color(0xFFC8E6C9),
|
||||||
|
secondary = Color(0xFF43A047),
|
||||||
|
background = Color(0xFFFAFAFA),
|
||||||
|
surface = Color.White,
|
||||||
|
surfaceContainerHigh = Color(0xFFF5F5F5),
|
||||||
|
onBackground = Color(0xFF212121),
|
||||||
|
onSurface = Color(0xFF212121),
|
||||||
|
onSurfaceVariant = Color(0xFF757575),
|
||||||
|
error = Color(0xFFD32F2F),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun YarisOBDTheme(
|
||||||
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
dynamicColor: Boolean = false, // Off by default — use our brand colors
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val colorScheme = when {
|
||||||
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
|
}
|
||||||
|
darkTheme -> DarkColorScheme
|
||||||
|
else -> LightColorScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
val view = LocalView.current
|
||||||
|
if (!view.isInEditMode) {
|
||||||
|
SideEffect {
|
||||||
|
val window = (view.context as Activity).window
|
||||||
|
window.statusBarColor = colorScheme.background.toArgb()
|
||||||
|
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">yaris-obd</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.YarisOBD" parent="android:Theme.Material.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">@android:color/black</item>
|
||||||
|
<item name="android:navigationBarColor">@android:color/black</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<network-security-config>
|
||||||
|
<!-- Allow cleartext to Pi hotspot (10.42.0.1) — local-only, no internet -->
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="false">10.42.0.1</domain>
|
||||||
|
<domain includeSubdomains="false">yaris.local</domain>
|
||||||
|
</domain-config>
|
||||||
|
</network-security-config>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application) apply false
|
||||||
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
|
alias(libs.plugins.kotlin.serialization) apply false
|
||||||
|
alias(libs.plugins.ksp) apply false
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
|
|
||||||
|
# AndroidX package structure
|
||||||
|
android.useAndroidX=true
|
||||||
|
|
||||||
|
# Kotlin code style
|
||||||
|
kotlin.code.style=official
|
||||||
|
|
||||||
|
# Compose compiler (managed by Kotlin plugin in AGP 8.x+)
|
||||||
|
# No longer need composeVersion here — managed automatically
|
||||||
|
|
||||||
|
# Disable build features we don't use
|
||||||
|
android.defaults.buildfeatures.buildconfig=true
|
||||||
|
android.defaults.buildfeatures.aidl=false
|
||||||
|
android.defaults.buildfeatures.renderscript=false
|
||||||
|
android.defaults.buildfeatures.resvalues=false
|
||||||
|
android.defaults.buildfeatures.shaders=false
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
[versions]
|
||||||
|
agp = "8.7.3"
|
||||||
|
kotlin = "2.1.0"
|
||||||
|
compose-bom = "2025.01.00"
|
||||||
|
compose-compiler = "1.5.15" # Kotlin 2.1.x
|
||||||
|
okhttp = "4.12.0"
|
||||||
|
retrofit = "2.11.0"
|
||||||
|
kotlinx-serialization = "1.7.3"
|
||||||
|
kotlinx-coroutines = "1.9.0"
|
||||||
|
room = "2.6.1"
|
||||||
|
workmanager = "2.10.0"
|
||||||
|
coil = "3.0.4"
|
||||||
|
maplibre = "11.7.0"
|
||||||
|
security-crypto = "1.1.0-alpha06"
|
||||||
|
lifecycle = "2.8.7"
|
||||||
|
activity-compose = "1.9.3"
|
||||||
|
core-ktx = "1.15.0"
|
||||||
|
navigation-compose = "2.8.5"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
# Compose BOM
|
||||||
|
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
||||||
|
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||||
|
compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
|
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
|
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
|
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
compose-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
|
||||||
|
|
||||||
|
# Navigation
|
||||||
|
navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation-compose" }
|
||||||
|
|
||||||
|
# Activity + Compose
|
||||||
|
activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
|
||||||
|
|
||||||
|
# Core
|
||||||
|
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
|
||||||
|
|
||||||
|
# Lifecycle
|
||||||
|
lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
||||||
|
lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
||||||
|
|
||||||
|
# Network
|
||||||
|
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||||
|
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
||||||
|
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
||||||
|
retrofit-kotlinx-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofit" }
|
||||||
|
|
||||||
|
# Serialization
|
||||||
|
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||||
|
|
||||||
|
# Coroutines
|
||||||
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
|
||||||
|
|
||||||
|
# Local storage
|
||||||
|
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||||
|
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||||
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||||
|
|
||||||
|
# Background work
|
||||||
|
workmanager = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workmanager" }
|
||||||
|
|
||||||
|
# Images
|
||||||
|
coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
|
||||||
|
coil-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" }
|
||||||
|
|
||||||
|
# Maps
|
||||||
|
maplibre = { group = "org.maplibre.gl", name = "android-sdk", version.ref = "maplibre" }
|
||||||
|
|
||||||
|
# Security
|
||||||
|
security-crypto = { group = "androidx.security", name = "security-crypto", version.ref = "security-crypto" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
|
ksp = { id = "com.google.devtools.ksp", version = "2.1.0-1.0.29" }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
+4
-4
@@ -69,13 +69,13 @@ android/
|
|||||||
|
|
||||||
## Sub-phases
|
## Sub-phases
|
||||||
|
|
||||||
### 28.1 — Scaffold + networking proof-of-concept ⬜ NOT STARTED
|
### 28.1 — Scaffold + networking proof-of-concept 🚧 IN PROGRESS
|
||||||
**Gate: dual-network binding must work on the real Pixel before anything else is built.**
|
**Gate: dual-network binding must work on the real Pixel before anything else is built.**
|
||||||
|
|
||||||
#### 28.1.1 — Project creation
|
#### 28.1.1 — Project creation
|
||||||
- [ ] Create Android project in `android/` via Android Studio (Empty Activity + Compose)
|
- [x] Create Android project in `android/` via Android Studio (Empty Activity + Compose)
|
||||||
- [ ] Kotlin DSL build files with version catalog
|
- [x] Kotlin DSL build files with version catalog
|
||||||
- [ ] `.gitignore` for Android (`*.iml`, `.gradle/`, `build/`, `local.properties`, `.idea/`)
|
- [x] `.gitignore` for Android (`*.iml`, `.gradle/`, `build/`, `local.properties`, `.idea/`)
|
||||||
- [ ] Verify project builds and runs on emulator (blank screen)
|
- [ ] Verify project builds and runs on emulator (blank screen)
|
||||||
- [ ] Commit: `init: android project scaffold`
|
- [ ] Commit: `init: android project scaffold`
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("com\\.android.*")
|
||||||
|
includeGroupByRegex("com\\.google.*")
|
||||||
|
includeGroupByRegex("androidx.*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "yaris-obd"
|
||||||
|
|
||||||
|
include(":app")
|
||||||
Reference in New Issue
Block a user