CardVault Wallet App - Android Development
Build premium offline-first Android wallet applications using modern Android development practices.
What This Skill Does
Guides development of CardVault-style Android wallet apps that manage digital cards (credit, debit, gift cards, vouchers, memberships) with complete offline operation, premium 60fps animations, OCR capabilities, and strict privacy standards. Uses Jetpack Compose, MVVM architecture, Room database, CameraX, and ML Kit.
Core Principles
1. **100% Offline Operation**: Never add network permissions, cloud APIs, or remote dependencies
2. **Privacy First**: No analytics, tracking, or data collection - reject third-party telemetry
3. **Premium Experience**: All animations target 60fps with smooth Material Design 3 transitions
4. **Local Storage Only**: All data stays on device in Room database and app's private storage
5. **Security**: Use Google Tink for encryption, sandboxed storage for all files
Technology Stack Requirements
**Language & Framework:**
Kotlin 100% (no Java) - use Kotlin idioms and modern syntaxJetpack Compose for all UI (no XML layouts except resources)Material Design 3 - strict adherence to M3 guidelines**Architecture:**
Pattern: MVVM + Unidirectional Data Flow (UDF)Layers: Strict separation - Data → Domain → PresentationDI: Hilt with `@HiltAndroidApp`, `@AndroidEntryPoint`, `@HiltViewModel`Navigation: Compose Navigation with kotlinx.serialization for type-safe argumentsAsync: Kotlin Coroutines + Flow/StateFlow for reactive streams**Key Libraries:**
Database: Room with KSP annotation processingPreferences: Proto DataStore (NOT SharedPreferences)Camera: CameraX for lifecycle-aware implementationML: Google ML Kit Text Recognition for offline OCRSecurity: Google Tink for backup encryptionImages: Coil for loading, caching, transformationsSerialization: kotlinx.serialization for JSON and navigation args**Build Configuration:**
Target SDK: API 36 (Android 15)Min SDK: API 29 (Android 10)Java Version: 11Build System: Gradle Kotlin DSL (`build.gradle.kts`)Dependencies: Centralized in `gradle/libs.versions.toml`Package Structure
```
com.technitedminds.wallet/
├── MainActivity.kt # @AndroidEntryPoint entry point
├── WalletApplication.kt # @HiltAndroidApp application class
├── data/ # Data layer implementations
│ ├── local/
│ │ ├── database/ # Room entities, DAOs, converters, WalletDatabase
│ │ ├── files/ # ImageFileManager for file operations
│ │ └── preferences/ # Proto DataStore managers
│ ├── repository/ # Repository implementations (*RepositoryImpl.kt)
│ ├── mapper/ # Entity ↔ Domain model mapping
│ └── ocr/ # ML Kit text recognition
├── domain/ # Business logic layer
│ ├── model/ # Domain models (Card, CardType, Category)
│ ├── repository/ # Repository interfaces
│ ├── usecase/ # Business logic use cases
│ └── util/ # Domain utilities
├── presentation/ # UI layer
│ ├── screens/ # Feature screens (*Screen.kt, *ViewModel.kt)
│ ├── components/ # Reusable composables
│ └── navigation/ # Navigation setup
├── di/ # Hilt modules (@Module, @InstallIn)
├── ui/theme/ # M3 theming (Color, Theme, Type)
└── utils/ # Extensions and utilities
```
Step-by-Step Implementation Guide
1. Project Setup
**Create project structure:**
Set up Gradle with Kotlin DSL and Version Catalog (`gradle/libs.versions.toml`)Configure Hilt, Room, CameraX, ML Kit, Coil dependenciesCreate `WalletApplication.kt` with `@HiltAndroidApp`Create `MainActivity.kt` with `@AndroidEntryPoint`Set up Material Design 3 theme with dynamic color support2. Domain Layer (Business Logic)
**Define domain models:**
```kotlin
data class Card(
val id: Long = 0,
val name: String,
val type: CardType,
val categoryId: Long,
val imagePaths: List<String>, // Front and back image paths
val extractedData: Map<String, String>, // OCR results for textual cards
val customFields: Map<String, String>, // User-added fields
val gradient: CardGradient?, // For textual cards display
val createdAt: Long,
val updatedAt: Long
)
sealed class CardType {
object Credit : CardType() // OCR-enabled textual card
object Debit : CardType() // OCR-enabled textual card
object TransportCard : CardType() // Image-only
object GiftCard : CardType() // Image-only
object LoyaltyCard : CardType() // Image-only
// ... 11 more image-only types
data class Custom(val name: String) : CardType()
}
data class Category(
val id: Long = 0,
val name: String,
val iconResId: Int,
val colorHex: String,
val isDefault: Boolean
)
```
**Create repository interfaces:**
`CardRepository` - CRUD operations for cards`CategoryRepository` - Category management`PreferencesRepository` - App settings**Implement use cases:**
`AddCardUseCase` - validates input, saves card with images`GetCardsUseCase` - retrieves cards with filtering/sorting`ExtractCardDataUseCase` - runs OCR on Credit/Debit cards only`DeleteCardUseCase` - removes card and cleans up images3. Data Layer Implementation
**Room Database:**
Create `@Entity` classes with proper annotationsDefine `@Dao` interfaces with `@Query`, `@Insert`, `@Update`, `@Delete`Implement type converters for `CardType`, `Map<String, String>`, `List<String>`Create `WalletDatabase` with `@Database` annotationReturn `Flow<T>` for reactive queries**Image File Manager:**
Store images in `context.filesDir/images/`Naming: `{cardId}_front.jpg`, `{cardId}_back.jpg`Compress images (JPEG quality 85) before savingClean up orphaned images on card deletion**Proto DataStore:**
Define `.proto` schema for user preferencesCreate DataStore manager with suspend functionsHandle theme preference, default categories, app settings**Repository Implementations:**
Map Room entities to domain modelsInject DAOs and file manager via constructorHandle exceptions and return `Result<T>` or domain modelsUse `@Inject constructor()` for Hilt injection4. Dependency Injection (Hilt)
**Create modules:**
`DatabaseModule`: Provides `WalletDatabase`, DAOs with `@Singleton``RepositoryModule`: Binds repository interfaces to implementations with `@Binds``CameraModule`: Provides CameraX and ML Kit dependenciesNO network module - app is completely offline**Apply annotations:**
`@Module` + `@InstallIn(SingletonComponent::class)` for singleton modules`@Provides` for external dependencies`@Binds` for interface → implementation mapping5. Camera & OCR Implementation
**CameraX Setup:**
Request `CAMERA` permission with rationaleCreate lifecycle-aware CameraX providerProvide card overlay guides (16:9, 4:3, 3:4 aspect ratios)Support flash, focus, exposure controlsAllow preview and retake before confirming**ML Kit OCR (Credit/Debit Only):**
Initialize Text Recognition API (offline model)Extract: card number (15-16 digits), expiry (MM/YY), CVV (3-4 digits), nameValidate with regex patternsProvide confidence scores for each fieldShow processing indicator during recognitionAllow manual correction if OCR fails**Important:** Never run OCR on image-only card types (Gift, Voucher, Membership, etc.)
6. Presentation Layer (UI)
**ViewModels:**
Annotate with `@HiltViewModel`Inject use cases via constructorExpose `StateFlow<UIState>` for UI stateUse `SharedFlow<Event>` for one-time eventsUpdate state immutably using `copy()` on data classesUse `viewModelScope` for coroutines**Composable Screens:**
Home: LazyColumn with card grid, category filters, searchAddCard: Card type selection → Camera capture → Data reviewCardDetail: Card flip animation, image viewer, edit fieldsCategories: Category CRUD with color pickerSettings: Theme, backup/restore, about**Reusable Components:**
`CardFlipAnimation` - 300ms 3D flip with `graphicsLayer``CameraPreview` - CameraX preview with overlay guides`CardGradientView` - Gradient card design for textual cards`ImageViewer` - Pinch-to-zoom, swipe between front/back**Navigation:**
Define routes as `@Serializable` data classes/objectsUse `NavController` and `NavHost`Bottom navigation: Home, Categories, SettingsFloating destinations: AddCard, CardDetail, CameraPass arguments via kotlinx.serialization7. Card Workflows
**Textual Cards (Credit/Debit):**
1. User selects Credit or Debit type
2. Camera captures front and back images
3. ML Kit OCR extracts card data
4. User reviews and edits extracted data
5. System generates gradient card design
6. Images stored, card saved to database
**Image-Only Cards (All Others):**
1. User selects card type (Gift, Voucher, etc.)
2. Camera captures front and back images
3. No OCR processing - pure image storage
4. User adds custom fields if needed
5. Images stored as-is for sharing
6. Card saved to database
8. Performance & Animation
**Performance Targets:**
60fps animations on mid-range devicesCold start < 3 secondsSmooth LazyColumn scrolling with keys and item animationsEfficient Coil image loading with placeholdersMemory-conscious bitmap handling**Animation Standards:**
Card flip: 300ms with `FastOutSlowInEasing`List animations: Spring physics for natural motionTransitions: Fade, slide, scale with Material Motion3D effects: Use `graphicsLayer` for rotationYGesture support: Swipe, pinch-to-zoom with feedback**Material Design 3:**
Follow M3 color system with dynamic color supportUse M3 components: Card, Button, FAB, TextField, DialogImplement proper elevation and shadowsAdd ripple effects on interactive elementsSupport light and dark themesEnsure minimum touch targets (48dp)Code Style & Conventions
**Naming:**
Activities: `*Activity.kt`ViewModels: `*ViewModel.kt` with `@HiltViewModel`Repositories: `*Repository.kt` (interface) + `*RepositoryImpl.kt` (impl)Use Cases: `*UseCase.kt`Entities: `*Entity.kt` with `@Entity`DAOs: `*Dao.kt` with `@Dao`Composables: PascalCase (e.g., `CardFlipAnimation`)Screens: `*Screen.kt` with corresponding `*ViewModel.kt`**Kotlin Style:**
Prefer `val` over `var` for immutabilityUse trailing commas in multi-line constructsMaximum line length: 120 charactersUse meaningful names - avoid abbreviationsAdd KDoc for public APIsUse sealed classes/interfaces for type-safe statePrefer data classes for modelsUse extension functions for utilities**Compose Best Practices:**
Use `remember`, `derivedStateOf`, `LaunchedEffect` appropriatelyKeep composables small and focusedHoist state up - pass state down, events upUse `collectAsStateWithLifecycle()` for Flow → StateAdd `@Composable` preview functionsUse `Modifier` parameter last in signaturesAvoid side effects in compositionSecurity & Privacy
**Security Requirements:**
Never log sensitive card data (numbers, CVV, names)Encrypt backup files with Google TinkStore all files in sandboxed private storageDon't use external storage or public directoriesValidate all user inputs before savingHandle exceptions without exposing stack traces**Privacy Guarantees:**
No network permissions in AndroidManifest.xmlNo analytics or tracking librariesNo third-party SDKs with data collectionAll data stays on deviceNo cloud sync or remote APIsCommon Pitfalls to Avoid
❌ Don't add network permissions or cloud APIs❌ Don't use SharedPreferences - use Proto DataStore❌ Don't block main thread - always use coroutines for I/O❌ Don't expose mutable state from ViewModels❌ Don't run OCR on non-Credit/Debit cards❌ Don't forget to clean up images when deleting cards❌ Don't skip type converters for complex Room types❌ Don't put business logic in composables or repositories❌ Don't forget to handle camera/storage permissions❌ Don't skip error handling in async operationsTesting Strategy
**Unit Tests:**
Test ViewModels with fake/mock repositoriesTest use cases with mock dependenciesTest mappers for correct transformationTest utilities and extensionsUse MockK for mockingAssert on state changes and side effects**Integration Tests:**
Test Room database operations end-to-endTest repository implementations with real databaseVerify data layer to domain layer mappingTest file operations with temporary directories**UI Tests:**
Use Compose testing APIs (`composeTestRule`)Test user interactions and navigation flowsVerify UI state updates correctlyTest accessibility with semanticsDebugging Commands
```bash
Build debug APK
./gradlew assembleDebug
Install and run
./gradlew installDebug
Run unit tests
./gradlew test
Run instrumented tests
./gradlew connectedAndroidTest
Generate KSP sources (for Room, Hilt)
./gradlew kspDebugKotlin
Clean build
./gradlew clean
```
Constraints
**Android-specific**: This skill is for Android development only**Kotlin required**: No Java code allowed**Offline-only**: Absolutely no network operations**Jetpack Compose**: Must use Compose for UI (no XML layouts)**MVVM architecture**: Strict adherence to layered architecture**Material Design 3**: Follow M3 guidelines strictly**Performance**: Must target 60fps animations**Privacy**: No data collection or tracking allowed