feat(android): implement full Android frontend

- Build config: build.gradle (app + root), settings.gradle, AndroidManifest
- Application entry: PantreeApplication (Hilt), MainActivity (Compose host)
- Data layer:
  - ApiService (Retrofit interface, all 20 endpoints)
  - ApiModels (all request/response DTOs)
  - AuthInterceptor (JWT Bearer header injection)
  - TokenStore (EncryptedSharedPreferences)
  - SyncPreferences (DataStore, last-sync timestamp)
  - PantreeDatabase (Room, 5 entities)
  - Entities: PantryItemEntity, RecipeEntity, RecipeIngredientEntity, ShoppingListEntity, ShoppingListItemEntity
  - DAOs: PantryDao, RecipeDao, ShoppingListDao
  - Repositories: AuthRepository, PantryRepository, RecipeRepository, ShoppingListRepository, SyncRepository
- DI: AppModule (Hilt, provides OkHttp/Retrofit/Room/DAOs)
- Util: Result<T> sealed class, safeApiCall, toUserMessage (human-readable error mapping)
- Sync: SyncManager (DefaultLifecycleObserver, triggers on app open/foreground)
- Theme: Color, Type, Theme (Material 3, light + dark)
- Navigation: Screen sealed class, PantreeNavGraph (deep links for password reset)
- Shared components: LoadingState, ErrorState, EmptyState, OfflineBanner, PantreeTopBar
- Auth screens: SplashScreen, SignInScreen, SignUpScreen, ForgotPasswordScreen, ResetPasswordScreen + AuthViewModel
- Pantry screen: PantryScreen (list/add/edit/delete dialogs) + PantryViewModel
- Recipe screens: RecipesScreen (filter chips, search, availability badges) + RecipeDetailScreen (scale 1x/2x/3x, ingredient pantry status) + RecipeViewModel
- Shopping screens: ShoppingListsScreen + ShoppingListDetailScreen (check-off, progress bar, merge feedback, add-from-recipes) + ShoppingListViewModel
- Settings screen: sign out + account deletion (15-day window copy) + SettingsViewModel
- Tests: ResultTest (unit), RecipeDetailScreenTest (unit), PantryScreenTest (instrumented)
- README: architecture, module structure, UI states table, sync strategy, setup

Every screen handles loading / error / empty / success states.
Offline: cached Room data shown read-only.
This commit is contained in:
Azriel
2026-05-10 15:09:12 +00:00
parent e633d693da
commit 75db2ea8f5
51 changed files with 4138 additions and 0 deletions

128
android/app/build.gradle Normal file
View File

@@ -0,0 +1,128 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.dagger.hilt.android'
id 'com.google.devtools.ksp'
}
android {
namespace 'com.pantree.app'
compileSdk 34
defaultConfig {
applicationId "com.pantree.app"
minSdk 26
targetSdk 34
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
buildConfigField "String", "BASE_URL", '"https://api.pantree.app/v1/"'
buildConfigField "String", "GOOGLE_WEB_CLIENT_ID", '"YOUR_GOOGLE_WEB_CLIENT_ID"'
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
buildConfigField "String", "BASE_URL", '"http://10.0.2.2:3000/v1/"'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
buildFeatures {
compose true
buildConfig true
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.11'
}
packaging {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
// Core
implementation 'androidx.core:core-ktx:1.13.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.9.0'
// Compose BOM
implementation platform('androidx.compose:compose-bom:2024.04.01')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.material:material-icons-extended'
// Navigation
implementation 'androidx.navigation:navigation-compose:2.7.7'
// Hilt DI
implementation 'com.google.dagger:hilt-android:2.51.1'
ksp 'com.google.dagger:hilt-android-compiler:2.51.1'
implementation 'androidx.hilt:hilt-navigation-compose:1.2.0'
// Room
implementation 'androidx.room:room-runtime:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
ksp 'androidx.room:room-compiler:2.6.1'
// Retrofit + OkHttp
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
implementation 'com.squareup.retrofit2:converter-gson:2.11.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
// Gson
implementation 'com.google.code.gson:gson:2.10.1'
// ViewModel + StateFlow
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.7.0'
// EncryptedSharedPreferences
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
// Google Identity (One Tap)
implementation 'com.google.android.gms:play-services-auth:21.1.1'
// Coil (image loading)
implementation 'io.coil-kt:coil-compose:2.6.0'
// DataStore
implementation 'androidx.datastore:datastore-preferences:1.1.0'
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0'
// Testing
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0'
testImplementation 'io.mockk:mockk:1.13.10'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation platform('androidx.compose:compose-bom:2024.04.01')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
}