A premium, modern habit tracking Android application built with Jetpack Compose and Material 3.
HabiTrek helps users build and maintain daily habits through a clean, interactive interface with 3D card animations, calendar-based streak tracking, and a built-in web search feature for habit-related content.
- AI Summary (Gemini Cloud) β An interactive, expandable/collapsible
AiSummaryCardthat uses Google'sgemini-2.5-flash-litemodel to provide a personalized, intelligent summary of the user's habits. It automatically expands when generating a new summary and defaults to an expanded view for quick insights. Generates in ~1s using a strictly enforced system prompt to prevent hallucinations. - Habit List β All habits displayed in a unified card block with custom rounded corners (top card gets large top rounding, bottom card gets large bottom rounding, middle cards get uniform rounding), creating a cohesive visual stack.
- 3D Tilt Animation β Each habit card responds to touch with a real-time 3D tilt effect. Using
Animatable,graphicsLayer, andpointerInput, the card tilts based on finger position relative to center, creating a tactile, premium feel. - Checkmark Toggle β A circular checkmark button with animated color fill and scale-on-press animation. Toggling marks the habit as completed for today, syncing immediately with the local Room database.
- Empty State β When no habits exist, a centered butterfly icon with "No Habits Added Yet!" text provides a friendly, non-empty first impression.
- Sealed UI State β The screen uses a sealed interface (
Loading,Success,Error,Empty) to cleanly handle all possible screen states without ambiguity.
- Habit Name Input β Outlined text field with keyboard dismiss on Done action.
- Duration Input β Numeric keyboard input with automatic validation (capped at 1440 minutes / 24 hours). A companion display box shows the duration converted to a human-readable "Xh Ym" format in real-time.
- Color Picker β Horizontally scrollable row of color balls. Users select a color to personalize their habit card. A "Use System Default Color Scheme" button resets to the device's Material You primary color.
- Live Preview β A preview card at the bottom shows exactly how the habit will appear on the Home Screen, updating in real-time as the user types.
- Validation β The "Create Habit" button is disabled until both name and duration are provided.
- Hero Header β Editable habit name (inline
OutlinedTextFieldwith transparent borders) paired with a large checkmark button for today's completion toggle. - Metric Cards β Two side-by-side cards displaying "Daily Goal" (duration) and "Total Days" (total completed days count).
- Interactive Calendar β A month-by-month calendar grid showing completed days with filled color circles. Users can navigate between months and tap individual days to toggle completion status retroactively.
- Duration & Color Editing β The same duration input and color picker UI from the Add screen, allowing users to modify habits after creation.
- Debounced Saves β Name, duration, and color changes are debounced (700ms delay) before persisting to the database, preventing excessive writes during rapid typing.
- Delete Habit β A destructive action requiring the user to type "delete" (case-insensitive) in a confirmation dialog before permanently removing the habit and all its completions (cascading delete via Room ForeignKey).
- Assisted Injection β The ReviewViewModel uses Hilt's
@AssistedInjectto receive the runtimehabitIdparameter from navigation, enabling proper ViewModel scoping per habit.
- GNews API Integration β Search for habit-related articles and content using the GNews API via Retrofit.
- Search Bar β Custom search bar with loading indicator that replaces the search icon during active requests.
- Result Cards β Search results displayed in the same rounded card block style as the home screen, maintaining visual consistency.
- Sealed State β Uses
Idle,Loading,Success, andErrorsealed states for clean UI handling.
- Navigation 3 β Uses Jetpack's latest Navigation 3 API (
NavDisplay,NavEntry) with a customNavigationViewModelmanaging an in-memory backstack. - Bottom Navigation Bar β Floating bottom app bar with adaptive padding for both gesture and 3-button navigation modes using
WindowInsets. - Floating Action Button β Visible only on the Home screen for adding new habits.
- Dynamic Top Bar β Top bar content changes based on the current screen/route.
HabiTrek follows Clean Architecture with a strict unidirectional dependency rule:
Presentation β Domain β Data
The Presentation layer depends on Domain. The Data layer depends on Domain. The Domain layer depends on nothing.
| Component | Purpose |
|---|---|
HabitDao |
Room DAO for habit CRUD operations |
CompletionDao |
Room DAO for completion records (marking days) |
AiSummaryDao |
Room DAO for caching daily AI summaries |
HabitEntity |
Room entity for the habit_table |
CompletionEntity |
Room entity for the completions table with ForeignKey cascade |
AiSummaryEntity |
Room entity for caching summaries to prevent excessive API calls |
HabitEntityMapper |
Extension functions: Habit.toEntity(), HabitEntity.toDomain(), Flow<List<HabitEntity>>.toFlowListHabit() |
CompletionEntityMapper |
Extension functions: Completion.toEntity(), Flow<List<CompletionEntity>>.toFlowCompletionList() |
HabitRepositoryImpl |
Implements domain HabitRepository interface, handles entity β domain mapping |
CompletionRepositoryImpl |
Implements domain CompletionRepository interface |
SearchRepositoryImpl |
Implements domain SearchRepository, calls GNews API via Retrofit |
AiSummaryRepositoryImpl |
Formats habit data into prompts, calls GeminiSummarizer, and caches responses via AiSummaryDao |
GeminiSummarizer |
Minimal wrapper around the official com.google.ai.client.generativeai SDK |
HabitTrackerAppDatabase |
Room database exposing all DAOs |
SearchApi |
Retrofit interface for GNews API |
| Component | Purpose |
|---|---|
Habit |
Core domain model β id, name, color (as HabitColor), durationMinutes |
HabitColor |
Value wrapper for color as ULong |
Completion |
Domain model β id, habitId, dateMillis |
HabitListWithTodayStatusList |
Bundles a list of habits with a set of completed habit IDs for today |
HabitWithTodayStatus |
Bundles completion timestamps with an isCompletedToday boolean |
HabitRepository |
Interface for habit CRUD |
CompletionRepository |
Interface for completion CRUD and queries |
SearchRepository |
Interface for web search, returns List<SearchArticle> |
AiSummaryRepository |
Interface for AI summary generation and caching |
GetHabitsWithTodayStatusUseCase |
Combines habits flow + today's completions flow into HabitListWithTodayStatusList |
GetHabitCompletionsUseCase |
Maps all completions for a habit into HabitWithTodayStatus |
ToggleHabitCompletionUseCase |
Checks if completion exists β deletes or creates accordingly |
CreateHabitUseCase |
Delegates habit creation to repository |
GenerateAiSummaryUseCase |
Calls repo to generate a new AI summary via network |
GetCachedSummaryUseCase |
Retrieves today's cached summary without network calls |
Each feature has its own package with Screen, ViewModel, model/ (containing UiState, UiModel, Mapper).
| Feature | Key Components |
|---|---|
featureHomeScreen |
HomScreen, HomeViewModel, HomeUiState (sealed), HomeUiModel, HomeUiModelMapper, HabitCard, AiSummaryCard |
featureAddHabitScreen |
AddHabitScreen, AddHabitViewModel, AddHabitUiState, AddHabitUiModel, HabitCardPreview, ColorBall |
featureReviewScreen |
ReviewScreen, ReviewViewModel (AssistedInject), ReviewUiState (sealed), ReviewUiModel, ReviewUiModelMapper, SimpleCalendarGrid, MetricCard |
featureWebSearch |
SearchScreen, SearchViewModel, SearchScreenState (sealed), SearchArticle (domain), SearchResultItem |
navigation |
HabiTrekNavHost β Navigation 3 host with NavDisplay |
| Component | Purpose |
|---|---|
di/AppModule |
Hilt module providing Room database, DAOs, Retrofit, and SearchApi |
di/RepositoryModule |
Hilt @Binds module mapping interfaces to implementations |
ui/components/ |
Shared composables: CheckMarkButton, HabiTrekSurface, HabiTrekSectionThumbnail, ExpressiveIconButton, HabiTrekNavigationBar, HabiTrekFloatingActionButton, HabiTrekAppTopBar |
ui/navigation/ |
NavRoutes (sealed interface), NavigationViewModel |
theme/ |
Material 3 theme: Color.kt, Type.kt, Shapes.kt, Theme.kt |
| Category | Technology |
|---|---|
| Language | Kotlin |
| UI Framework | Jetpack Compose (Material 3) |
| Architecture | Clean Architecture + MVVM + UDF (Unidirectional Data Flow) |
| DI | Hilt (Dagger) |
| Database | Room (SQLite) |
| Networking | Retrofit + Gson |
| AI Integration | Google Gemini SDK (com.google.ai.client.generativeai) |
| Navigation | Jetpack Navigation 3 |
| Async | Kotlin Coroutines + Flow |
| State Management | StateFlow + MutableStateFlow |
| Animations | Animatable, animateColorAsState, animateFloatAsState, graphicsLayer |
| Column | Type | Notes |
|---|---|---|
id |
Int |
Primary key, auto-generated |
name |
String |
Habit name |
color |
Long |
Compose Color value stored as Long |
duration_minutes |
Int |
Daily time allocation in minutes |
| Column | Type | Notes |
|---|---|---|
id |
Int |
Primary key, auto-generated |
habitId |
Int |
Foreign key β habit_table.id (CASCADE on delete) |
dateMillis |
Long |
UTC midnight timestamp of the completed day |
Colors are stored as Compose Color.value (ULong, 64-bit float representation). The app provides a palette of 15 preset colors. Index 0 represents "System Default" (stored as 0UL), which resolves to MaterialTheme.colorScheme.primary at render time. The palette is defined as a companion object constant in both AddHabitViewModel and ReviewViewModel.
-
isCompletedTodayis NOT in the domain model. It is a computed property that exists only in UI models. The domainHabitclass contains only database-persisted fields. Completion status is computed by UseCases and injected during the domain β UI mapping step. -
Debounced persistence for editable fields. Name, duration, and color changes in the ReviewScreen use a
Job?.cancel()+delay(700)pattern to coalesce rapid user changes into a single database write. -
Mutex for toggle operations. Both HomeViewModel and ReviewViewModel use a
Mutexaround toggle operations to prevent double-click race conditions where a rapid second tap could fire before the first database check completes. -
Navigation 3 with custom ViewModel-managed backstack. Rather than using the older NavController, the app uses Navigation 3's
NavDisplaywith anSnapshotStateListbackstack managed by aNavigationViewModel, enabling programmatic push/pop. -
Assisted Injection for ReviewViewModel. The
habitIdis a runtime navigation argument, not a Hilt-managed dependency.@AssistedInject+@AssistedFactorybridges this gap cleanly. -
Dynamic Timestamp Calculation. UseCases (like
GetHabitCompletionsUseCase) dynamically calculatetodayDateMillisupon invocation rather than relying on static or ViewModel-level properties. This prevents stale date bugs when the app is left open in the background across midnight.
Originally, this project experimented with 100% on-device AI inference using Google's LiteRT (formerly TFLite) and the gemma-1b-it-int4 model. While theoretically ideal for privacy, the reality on mobile hardware was challenging:
- Download Overhead: Users were forced to download a ~550MB
.taskfile before they could generate their first summary. - Resource Exhaustion: Instantiating the 1B parameter model locally consumed massive amounts of RAM, frequently triggering Android's
onTrimMemorycallbacks and causing native C++ crashes on mid-tier devices. - Severe Hallucinations: Because the 1B model lacked the reasoning capacity of larger cloud models, it struggled to adhere strictly to the JSON data schema. It frequently hallucinated habit names, invented long streaks that didn't exist in the database, and ignored formatting rules (like "no bullet points").
The Pivot: We fully migrated to the cloud-based Gemini API (gemini-2.5-flash-lite) using the official Android SDK.
- The app size dropped significantly.
- Responses are now nearly instantaneous (~1s) instead of freezing the UI during inference.
- The massive context window and superior instruction-following of Flash 2.5 means the AI never hallucinates data and strictly adheres to our custom prompt rules, resulting in a much more magical user experience.
# Clone the repository
git clone <repository-url>
# Open in Android Studio (Ladybug or later recommended)
# Sync Gradle
# Run on emulator or physical device (API 26+)com.liftley.habitrek/
βββ HabiTrekApplication.kt
βββ MainActivity.kt
βββ core/
β βββ di/ # Hilt modules
β βββ theme/ # Material 3 theme files
β βββ ui/
β βββ components/ # Shared composables
β βββ navigation/ # NavRoutes, NavigationViewModel
βββ data/
β βββ di/ # (empty, modules in core/di)
β βββ local/
β β βββ dao/ # HabitDao, CompletionDao, AiSummaryDao
β β βββ database/ # HabitTrackerAppDatabase
β β βββ entity/ # Entities (Habit, Completion, AiSummary) + Mappers
β βββ remote/
β β βββ api/ # SearchApi + GNews DTOs
β β βββ dto/ # (placeholder for future DTOs)
β βββ repository/ # Repository implementations
βββ domain/
β βββ model/ # Habit, Completion, HabitColor, SearchArticle, etc.
β βββ repository/ # Repository interfaces
β βββ usecase/ # Business logic use cases
βββ presentation/
βββ featureAddHabitScreen/
β βββ components/ # ColorBall, HabitCardPreview
β βββ model/ # AddHabitUiState, AddHabituiModel
βββ featureHomeScreen/
β βββ components/ # HabitCard, AiSummaryCard
β βββ model/ # HomeUiState, HomeUiModel, Mapper
βββ featureReviewScreen/
β βββ components/ # SimpleCalendarGrid, MetricCard
β βββ model/ # ReviewUiState, ReviewUiModel, Mapper
βββ featureWebSearch/
β βββ components/ # SearchResultItem
β βββ model/ # SearchScreenState
βββ navigation/ # HabiTrekNavHost
βββ util/ # TimeUtils