A travel companion Android app that helps users explore famous buildings around the world. Users can browse buildings, view detailed information, track their visits, and maintain a bucket list of buildings they want to see.
This project demonstrates modern Android development practices and architectural patterns for the Udacity Android Nanodegree program. You will build a complete application that showcases:
- MVVM Architecture - Separation of concerns with ViewModels, LiveData, and Flow
- Room Database - Normalized relational database with foreign keys and indices
- Repository Pattern - Offline-first data management strategy
- Paging3 Library - Efficient pagination for large datasets
- Error Handling - Comprehensive error management with Result wrapper and Event pattern
- Navigation Component - Type-safe navigation with Safe Args
- Coroutines - Asynchronous op erations with proper lifecycle management
┌─────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Compose │ │ Compose │ │ Navigation │ │
│ │ Screens │ │ Components │ │ │ │
│ └──────┬───────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ │ observes LiveData/Flow │
│ ▼ │
│ ┌──────────────┐ │
│ │ ViewModels │◄─── Lifecycle-aware │
│ └──────┬───────┘ │
└─────────┼───────────────────────────────────────────────┘
│
│ calls repository methods
▼
┌─────────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Domain │ │ Repository │ │
│ │ Models │ │ Interface │ │
│ └──────────────┘ └──────┬───────┘ │
└─────────────────────────────┼───────────────────────────┘
│
│ implements
▼
┌─────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ LOCAL │ │ REMOTE │ │
│ │ │ │ │ │
│ │ Room DB │◄─────────────────┤ Retrofit │ │
│ │ - Entity │ caches data │ - API │ │
│ │ - DAO │ │ - DTOs │ │
│ │ - Database │ │ │ │
│ └──────────────┘ └──────────────┘ │
│ ▲ ▲ │
│ │ │ │
│ └────────────┬─────────────────────┘ │
│ │ │
│ ┌───────┴────────┐ │
│ │ Repository │ │
│ │ Implementation│ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────┘
A minimal starter project has been provided. It includes the following:
- Complete project structure with MVVM architecture setup
- Entity and DAO skeleton classes (annotations and queries required)
- Retrofit API integration (service and provider complete)
- All UI components - 4 Fragments, 4 ViewModels, adapters (implementation required)
- Navigation graph and layouts (all XML resources complete)
- Domain models and error handling patterns (complete, no changes needed)
Students will implement 50 TODOs spanning:
- Room database annotations and queries
- Paging3 RemoteMediator for network-database synchronization
- Complete repository implementation with offline-first architecture
- ViewModel initialization and interaction methods
- RecyclerView adapter binding logic
- Navigation setup in MainActivity
- Application repository setup
- Fragment UI implementation (BuildingDetailFragment)
Important
Recommended Implementation Order: The TODO comments in the code are numbered (TODO-1, TODO-2, etc.) to suggest a logical implementation sequence. While you can approach tasks in any order, following the numbered sequence will help ensure dependencies are handled smoothly. Start with the database setup (TODO-1), then move to the repository layer, and finally implement the UI components.
app/src/main/java/com/udacity/project/spire
├── data/
│ ├── model/ # Building, Location, Country data classes
│ └── repository/ # TODO: Create repository classes
├── database/
│ ├── dao/ # TODO: Create DAO interfaces
│ └── AppDatabase # TODO: Setup Room database
├── ui/
│ ├── buildings/ # TODO: BuildingsFragment and RecyclerView adapter
│ ├── visits/ # TODO: Implement VisitsFragment
│ └── statistics/ # TODO: Implement StatisticsFragment
└── MainActivity # TODO: Navigation setup
- Offline-First - All data cached locally, works without internet
- Empty States - Contextual feedback when lists are empty
- Loading States - Progress indicators during data loading
- Error Messages - User-friendly error feedback via Snackbar
- Swipe to Refresh - Pull-to-refresh on list screens
- Configuration Change Handling - Data survives screen rotations
Instructions for how to get a copy of the project running on your local machine.
// Core Android
androidx.core:core-ktx
androidx.appcompat:appcompat
com.google.android.material:material
// Navigation
androidx.navigation:navigation-fragment-ktx
androidx.navigation:navigation-ui-ktx
// Room Database
androidx.room:room-runtime
androidx.room:room-ktx
androidx.room:room-paging
// Retrofit
com.squareup.retrofit2:retrofit
com.squareup.retrofit2:converter-gson
// Coroutines
org.jetbrains.kotlinx:kotlinx-coroutines-core
org.jetbrains.kotlinx:kotlinx-coroutines-android
// Lifecycle Components
androidx.lifecycle:lifecycle-viewmodel-ktx
androidx.lifecycle:lifecycle-livedata-ktx
// Paging 3
androidx.paging:paging-runtime-ktx
// Image Loading
io.coil-kt:coil
-
Clone the Repository
git clone git@github.com:udacity/cd0636-Android-App-Components-and-Data-Handling-project.git cd cd0636-Android-App-Components-and-Data-Handling/project/solution -
Open in Android Studio
- Launch Android Studio
- Select "Open an Existing Project"
- Navigate to the
starterdirectory - Wait for Gradle sync to complete
-
Build the Project
./gradlew build
Unit Tests:
Run all the unit tests in the app.
./gradlew testInstrumented Tests:
Run all the instrumented tests in the app. Requires an Android device of emulator.
./gradlew connectedAndroidTestTest with Coverage:
Run all unit tests in the app with coverage.
./gradlew testDebugUnitTestCoverageIn this project, you will build Spire, a travel companion app that helps users explore famous buildings around the world. Users can browse buildings, view detailed information, track their visits, and maintain a bucket list of buildings they want to see.
By completing this project, you will demonstrate your ability to:
- Design and implement a normalized relational database using Room
- Implement the Repository pattern for data management
- Use Paging3 for efficient list pagination
- Handle errors gracefully throughout the app
- Implement MVVM architecture with ViewModels and LiveData/Flow
- Navigate between screens using the Navigation Component
- Display empty states and loading indicators
- Work with coroutines for asynchronous operations
4 Screens:
- Buildings Screen - Paginated list of all buildings
- Building Detail Screen - Detailed view with visit status controls (Visited, Bucket List, Not Visited)
- My Visits Screen - Filtered list with chip group to toggle between visit statuses
- Statistics Screen - Aggregated statistics (total buildings, visits, meters climbed)
1. Room Database Implementation
- Create
SpireDatabasewith three normalized tables:buildingstable with foreign key tocitiescitiestable with foreign key tocountriescountriestable as the root
- Implement proper foreign key constraints (
onDelete,onUpdate) - Add indices on foreign key columns for performance
2. DAO Implementation
Create DAOs with queries for:
BuildingDao: Paging source for buildings, filter by visit status, get building by ID with relationshipsCityDao: Get or create city by name and countryCountryDao: Get or create country by name
3. Repository Pattern
- Implement
BuildingRepositorywith offline-first architecture - Use
Result<T>wrapper for operations that can fail - Return
Flow<PagingData<Building>>for paginated building list - Return
FloworLiveDatafor other data sources - Use coroutines with proper dispatchers (
Dispatchers.IOfor DB/network)
1. ViewModels (4 ViewModels - one per screen)
- Expose data as
LiveDataorFlow - Implement error handling with Event wrapper pattern
- Use
viewModelScopefor coroutine launches - Handle loading states appropriately
2. Fragments (4 Fragments)
- Use View Binding for type-safe view access
- Set up RecyclerView with appropriate adapters
- Observe ViewModel data and update UI
- Display error messages via Snackbar
- Implement empty states for list screens
- Use Navigation Component with Safe Args
3. Adapters
- Create
BuildingPagingAdapterfor paginated list - Create
BuildingAdapterfor filtered lists - Implement
BuildingLoadStateAdapterfor loading/error footer - Use DiffUtil for efficient updates
1. Repository Level
- Wrap network/database operations in try-catch
- Return
Result.success()orResult.failure() - Use
.catchoperator for Flow error handling
2. ViewModel Level
- Create
ErrorEventdata class andEventwrapper - Expose error events via
LiveData<Event<ErrorEvent>> - Handle
Resulttypes from repository
3. UI Level
- Observe error events in Fragments
- Use
getContentIfNotHandled()to prevent duplicate display - Show user-friendly messages via Snackbar
1. Paging3
Note
Advanced Features (Optional): These features are not required to meet the project requirements but are excellent additions for students who finish early or want to challenge themselves further. They will not be part of the grading rubric.
- Use
PagerwithPagingConfigin repository - Connect
BuildingLoadStateAdapterwithwithLoadStateFooter() - Handle empty state based on
LoadState - Use
cachedIn(viewModelScope)to preserve state
2. Empty States
- Add empty state layouts to all list screens
- Toggle visibility based on data availability
- Show contextual messages based on filter state
3. Statistics
- Implement aggregated queries in DAO
- Calculate total buildings, visited count, bucket list count
- Sum total meters climbed from visited buildings
Your submission must include:
- Complete implementation of all TODO items in starter code
- Proper error handling in all ViewModels
- Empty state UI for all list screens (Buildings, My Visits)
- Working Paging3 implementation with LoadStateAdapter
- Functional navigation between all screens
- App compiles and runs without crashes
- Code follows Kotlin conventions (proper use of
val/var, null safety, etc.) - Comments explaining complex logic
Note
Testing (Optional but Recommended): While not required for project submission, writing tests for your Repository classes and ViewModels is excellent practice. Consider adding:
- Unit tests for Repository methods
- DAO tests using an in-memory database
- ViewModel tests for business logic
Rubric Checklist:
- Room database with normalized schema and foreign keys
- All DAOs implemented with correct queries
- Repository returns
Result<T>for error-prone operations - ViewModels expose immutable
LiveData(notMutableLiveData) - Error handling with Event wrapper pattern
- Paging3 with LoadStateAdapter connected
- Empty states on Buildings and My Visits screens
- Navigation with Safe Args between screens
- No memory leaks (binding cleaned up in
onDestroyView) - Proper coroutine usage (
viewModelScope, correct dispatchers)
- Clone the starter repository
- Review the provided code:
- Entities are pre-defined (
BuildingEntity,CityEntity,CountryEntity) - Layout files are provided
- Navigation graph is set up
- Entities are pre-defined (
- Follow TODO comments in the code to implement each feature
- Test as you go - run the app frequently to catch issues early
- Refer to solution code if you get stuck (but try to solve problems independently first)
- Start with the data layer - Get Room database and DAOs working first
- Test queries in isolation - Write unit tests for DAOs
- Build feature by feature - Complete one screen fully before moving to the next
- Use Repository pattern - Don't call DAOs directly from ViewModels
- Handle errors everywhere - Every network/database call can fail
- Clean up resources - Set
_binding = nullinonDestroyView() - Check the rubric frequently - Make sure you're meeting all requirements
- Room Database Guide
- Paging 3 Codelab
- ViewModel Guide
- Kotlin Coroutines Guide
- Navigation Component Guide
- Kotlin - Programming language
- Android Jetpack - Suite of libraries
- Room - Database library
- Paging 3 - Pagination library
- Retrofit - HTTP client
- Coroutines - Async programming
- Navigation Component - Fragment navigation
- Material Design 3 - Design system
- Coil - Image loading




