diff --git a/LeaseGuard/.gitignore b/LeaseGuard/.gitignore
new file mode 100644
index 0000000..b4736b3
--- /dev/null
+++ b/LeaseGuard/.gitignore
@@ -0,0 +1,4 @@
+.gradle
+build
+*.apk
+captures
diff --git a/LeaseGuard/app/build.gradle.kts b/LeaseGuard/app/build.gradle.kts
new file mode 100644
index 0000000..9d9b5d5
--- /dev/null
+++ b/LeaseGuard/app/build.gradle.kts
@@ -0,0 +1,67 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.kotlin.serialization)
+}
+
+android {
+ namespace = "com.leaseguard.android"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "com.leaseguard.android"
+ minSdk = 28
+ targetSdk = 35
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.ui)
+ implementation(libs.androidx.ui.graphics)
+ implementation(libs.androidx.ui.tooling.preview)
+ implementation(libs.androidx.material3)
+ implementation(libs.androidx.navigation.compose)
+ implementation(libs.kotlinx.serialization.json)
+
+ implementation(libs.androidx.room.runtime)
+ implementation(libs.androidx.room.ktx)
+ ksp(libs.androidx.room.compiler)
+
+ implementation(libs.androidx.work.runtime.ktx)
+ implementation(libs.billing.ktx)
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(platform(libs.androidx.compose.bom))
+}
diff --git a/LeaseGuard/app/src/main/AndroidManifest.xml b/LeaseGuard/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..282be56
--- /dev/null
+++ b/LeaseGuard/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/MainActivity.kt b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/MainActivity.kt
new file mode 100644
index 0000000..6f0a6cf
--- /dev/null
+++ b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/MainActivity.kt
@@ -0,0 +1,106 @@
+package com.leaseguard.android
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.animation.*
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.selection.DisableSelection
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Dashboard
+import androidx.compose.material.icons.filled.Description
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.navigation.NavDestination.Companion.hierarchy
+import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import com.leaseguard.android.ui.dashboard.DashboardScreen
+import com.leaseguard.android.ui.detail.LeaseDetailScreen
+import com.leaseguard.android.ui.settings.SettingsScreen
+import com.leaseguard.android.ui.theme.LeaseGuardTheme
+
+sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
+ object Dashboard : Screen("dashboard", "Dashboard", Icons.Default.Dashboard)
+ object Leases : Screen("leases", "Leases", Icons.Default.Description)
+ object Settings : Screen("settings", "Settings", Icons.Default.Settings)
+ object Detail : Screen("detail/{leaseId}", "Detail", Icons.Default.Description)
+ object Paywall : Screen("paywall", "Paywall", Icons.Default.Description)
+}
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContent {
+ LeaseGuardTheme {
+ DisableSelection {
+ val navController = rememberNavController()
+ val items = listOf(
+ Screen.Dashboard,
+ Screen.Leases,
+ Screen.Settings
+ )
+
+ Scaffold(
+ bottomBar = {
+ NavigationBar {
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentDestination = navBackStackEntry?.destination
+ items.forEach { screen ->
+ NavigationBarItem(
+ icon = { Icon(screen.icon, contentDescription = null) },
+ label = { Text(screen.label) },
+ selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
+ onClick = {
+ navController.navigate(screen.route) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ saveState = true
+ }
+ launchSingleTop = true
+ restoreState = true
+ }
+ }
+ )
+ }
+ }
+ }
+ ) { innerPadding ->
+ NavHost(
+ navController,
+ startDestination = Screen.Dashboard.route,
+ modifier = Modifier.padding(innerPadding),
+ enterTransition = { fadeIn() + slideInHorizontally { it } },
+ exitTransition = { fadeOut() + slideOutHorizontally { -it } },
+ popEnterTransition = { fadeIn() + slideInHorizontally { -it } },
+ popExitTransition = { fadeOut() + slideOutHorizontally { it } }
+ ) {
+ composable(Screen.Dashboard.route) {
+ DashboardScreen(navController)
+ }
+ composable(Screen.Leases.route) {
+ DashboardScreen(navController)
+ }
+ composable(Screen.Settings.route) {
+ SettingsScreen()
+ }
+ composable(Screen.Detail.route) { backStackEntry ->
+ val leaseId = backStackEntry.arguments?.getString("leaseId")?.toLong() ?: 0L
+ LeaseDetailScreen(leaseId, navController)
+ }
+ composable(Screen.Paywall.route) {
+ com.leaseguard.android.ui.components.PaywallScreen(onDismiss = { navController.popBackStack() })
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/BackupData.kt b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/BackupData.kt
new file mode 100644
index 0000000..f27830b
--- /dev/null
+++ b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/BackupData.kt
@@ -0,0 +1,10 @@
+package com.leaseguard.android.data
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class BackupData(
+ val tenants: List,
+ val leases: List,
+ val documents: List
+)
diff --git a/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/Entities.kt b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/Entities.kt
new file mode 100644
index 0000000..cafdfc7
--- /dev/null
+++ b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/Entities.kt
@@ -0,0 +1,50 @@
+package com.leaseguard.android.data
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.serialization.Serializable
+
+@Serializable
+@Entity(tableName = "tenants")
+data class Tenant(
+ @PrimaryKey(autoGenerate = true) val id: Long = 0,
+ val name: String,
+ val email: String,
+ val phone: String,
+ val property_address: String,
+ val unit_number: String,
+ val created_at: Long = System.currentTimeMillis()
+)
+
+@Serializable
+@Entity(tableName = "leases")
+data class Lease(
+ @PrimaryKey(autoGenerate = true) val id: Long = 0,
+ val tenant_id: Long,
+ val start_date: Long,
+ val end_date: Long,
+ val monthly_rent: Double,
+ val status: String = "active", // 'active', 'renewed'
+ val reminder_90_scheduled: Boolean = false,
+ val reminder_60_scheduled: Boolean = false,
+ val reminder_30_scheduled: Boolean = false,
+ val created_at: Long = System.currentTimeMillis()
+)
+
+@Serializable
+@Entity(tableName = "documents")
+data class Document(
+ @PrimaryKey(autoGenerate = true) val id: Long = 0,
+ val lease_id: Long,
+ val display_name: String,
+ val uri: String,
+ val doc_type: String, // 'lease_agreement', 'inspection', 'receipt', 'other'
+ val uploaded_at: Long = System.currentTimeMillis()
+)
+
+@Serializable
+@Entity(tableName = "app_meta")
+data class AppMeta(
+ @PrimaryKey val key: String,
+ val value: String
+)
diff --git a/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/LeaseDao.kt b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/LeaseDao.kt
new file mode 100644
index 0000000..f202d45
--- /dev/null
+++ b/LeaseGuard/app/src/main/kotlin/com/leaseguard/android/data/LeaseDao.kt
@@ -0,0 +1,92 @@
+package com.leaseguard.android.data
+
+import androidx.room.*
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface LeaseDao {
+ @Query("""
+ SELECT * FROM leases
+ JOIN tenants ON leases.tenant_id = tenants.id
+ WHERE leases.status = 'active'
+ ORDER BY leases.end_date ASC
+ """)
+ fun getActiveLeasesWithTenants(): Flow