A small Android app for browsing trending movies, searching, and managing favorites using the TMDB API.
The app authenticates with TMDB using a v4 Access Token (Bearer token).
- Create a free account at themoviedb.org.
- Go to Settings → API and copy your API Read Access Token.
- Add it to
local.propertiesin the project root (this file is git-ignored):
TMDB_ACCESS_TOKEN=eyJhbGciOiJIUzI1NiJ9...your_token_here- Build and run. The token is read at build time via
BuildConfig.TMDB_ACCESS_TOKEN.
The project follows Clean Architecture with three layers:
UI → Domain → Data
- Domain — Pure Kotlin. Use cases, domain models, and repository interfaces. No Android dependencies.
- Data — Repository implementations, Ktor remote data source (
TmdbApi), Room local cache, DTOs, and mappers. Depends on Domain only. - UI — Jetpack Compose screens, ViewModels, and UI state. Depends on Domain only.
Key libraries: Ktor (networking), Room (caching), Koin (DI), Coil (image loading), Navigation Compose.
Repositories follow an offline-first strategy: page 1 falls back to the Room cache on network error; subsequent pages are network-only.
- No Shared Element Transitions — The details screen prefers a backdrop image (different URL/resolution) over the list poster thumbnail, so a shared element transition between the list and details would only work when backdrop data is missing. This inconsistency was deemed worse than no transition, so standard slide animations are used instead.
- Destructive Room migrations —
fallbackToDestructiveMigrationis used. Schema changes will clear the local cache. Acceptable for a cache-only database, but not suitable if Room ever stores user data beyond favorites. - English-only — All strings are externalized to
strings.xmland ready for localization, but only an Englishvalues/resource is provided. - No pagination cache — Only page 1 of trending results is cached locally. Scrolling to later pages requires a network connection.
- Error messages from the domain layer are not localized —
NetworkErrorcarries English default messages. The UI layer could map error types tostringResourcecalls for full localization but currently passes the domain message through.
The task mentioned adding an "Open in TMDB" deep link from details. I wasn't entirely sure what "deep link" meant in this context — whether it referred to opening the TMDB website from the app, or opening the app from a TMDB website link. Since I understand both concepts, I implemented it both ways:
- App → Website: An "Open in TMDB" button on the movie details screen launches the browser to
https://www.themoviedb.org/movie/{id}. UsesIntent.createChooser()to avoid the link looping back into the app. - Website → App: An intent filter in the manifest intercepts
https://www.themoviedb.org/movie/*URLs and opens the corresponding movie details screen in the app. The movie ID is parsed from the URL slug (e.g./movie/1159559-scream-7→1159559).