From 40de580db0becbd3925acce3403ca725d22f9bde Mon Sep 17 00:00:00 2001 From: Pramod Bharti Date: Wed, 18 Oct 2023 19:38:25 +0530 Subject: [PATCH] movies repo complete --- .../filmo/data/di/AppContainer.kt | 11 +++- .../filmo/data/network/MoviesApiService.kt | 32 ++++++++++- .../data/repositories/MoviesRepository.kt | 8 ++- .../repositories/NetworkMoviesRepository.kt | 44 +++++++++++++- .../filmo/domain/MoviesUseCase.kt | 42 ++++++++++++++ .../pramodbharti/filmo/dummydata/DummyData.kt | 24 ++++---- .../filmo/ui/components/CarouselItem.kt | 7 ++- .../filmo/ui/components/MediaItemsRow.kt | 5 +- .../pramodbharti/filmo/ui/models/MediaItem.kt | 6 +- .../filmo/ui/screens/details/DetailsScreen.kt | 4 +- .../filmo/ui/screens/favs/FavItemsScreen.kt | 3 +- .../filmo/ui/screens/movies/MoviesScreen.kt | 57 ++++++++++++------- .../ui/screens/movies/MoviesViewModel.kt | 40 +++++++------ 13 files changed, 212 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt diff --git a/app/src/main/java/com/pramodbharti/filmo/data/di/AppContainer.kt b/app/src/main/java/com/pramodbharti/filmo/data/di/AppContainer.kt index b3e061a..c07fc62 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/di/AppContainer.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/di/AppContainer.kt @@ -6,14 +6,18 @@ import com.pramodbharti.filmo.data.network.HeaderInterceptor import com.pramodbharti.filmo.data.network.MoviesApiService import com.pramodbharti.filmo.data.repositories.MoviesRepository import com.pramodbharti.filmo.data.repositories.NetworkMoviesRepository +import com.pramodbharti.filmo.domain.MoviesUseCase +import kotlinx.coroutines.Dispatchers import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Converter import retrofit2.Retrofit interface AppContainer { val moviesRepository: MoviesRepository + val moviesUseCase: MoviesUseCase } class DefaultAppContainer : AppContainer { @@ -21,6 +25,7 @@ class DefaultAppContainer : AppContainer { private val client = OkHttpClient .Builder() .addInterceptor(HeaderInterceptor()) + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build() private val json = Json { @@ -39,7 +44,9 @@ class DefaultAppContainer : AppContainer { retrofit.create(MoviesApiService::class.java) } override val moviesRepository: MoviesRepository by lazy { - NetworkMoviesRepository(moviesApiService) + NetworkMoviesRepository(moviesApiService, Dispatchers.IO) + } + override val moviesUseCase: MoviesUseCase by lazy { + MoviesUseCase(moviesRepository) } - } \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/data/network/MoviesApiService.kt b/app/src/main/java/com/pramodbharti/filmo/data/network/MoviesApiService.kt index 8097926..285721a 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/network/MoviesApiService.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/network/MoviesApiService.kt @@ -1,10 +1,38 @@ package com.pramodbharti.filmo.data.network import com.pramodbharti.filmo.data.DISCOVER_MOVIES +import com.pramodbharti.filmo.data.network.MoviesApiService.Companion.TRENDING_MOVIES +import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.network.models.MoviesResponse import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query interface MoviesApiService { - @GET(DISCOVER_MOVIES) - suspend fun getDiscoverMovies(): MoviesResponse + @GET(TRENDING_MOVIES) + suspend fun getTrendingMovies(@Query("page") page: Int = 1): MoviesResponse + + @GET(NOW_PLAYING_MOVIES) + suspend fun getNowPlayingMovies(@Query("page") page: Int = 1): MoviesResponse + + @GET(POPULAR_MOVIES) + suspend fun getPopularMovies(@Query("page") page: Int = 1): MoviesResponse + + @GET(TOP_RATED_MOVIES) + suspend fun getTopRatedMovies(@Query("page") page: Int = 1): MoviesResponse + + @GET(UPCOMING_MOVIES) + suspend fun getUpcomingMovies(@Query("page") page: Int = 1): MoviesResponse + + @GET(MOVIE_DETAILS) + suspend fun getMovieDetails(@Path("movie_id") movieId: Int): MovieResponse + + companion object { + const val TRENDING_MOVIES = "trending/movie/day" + const val NOW_PLAYING_MOVIES = "movie/now_playing" + const val POPULAR_MOVIES = "movie/popular" + const val TOP_RATED_MOVIES = "movie/top_rated" + const val UPCOMING_MOVIES = "movie/upcoming" + const val MOVIE_DETAILS = "movie/{movie_id}" + } } \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/data/repositories/MoviesRepository.kt b/app/src/main/java/com/pramodbharti/filmo/data/repositories/MoviesRepository.kt index a7b7750..3218432 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/repositories/MoviesRepository.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/repositories/MoviesRepository.kt @@ -1,7 +1,13 @@ package com.pramodbharti.filmo.data.repositories +import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.network.models.MoviesResponse interface MoviesRepository { - suspend fun getDiscoverMovies(): MoviesResponse + suspend fun getTrendingMovies(): MoviesResponse + suspend fun getNowPlayingMovies(): MoviesResponse + suspend fun getPopularMovies(): MoviesResponse + suspend fun getUpcomingMovies(): MoviesResponse + suspend fun getTopRatedMovies(): MoviesResponse + suspend fun getMovieDetails(movieId: Int): MovieResponse } \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/data/repositories/NetworkMoviesRepository.kt b/app/src/main/java/com/pramodbharti/filmo/data/repositories/NetworkMoviesRepository.kt index d0728f9..7ecd99e 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/repositories/NetworkMoviesRepository.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/repositories/NetworkMoviesRepository.kt @@ -1,7 +1,47 @@ package com.pramodbharti.filmo.data.repositories import com.pramodbharti.filmo.data.network.MoviesApiService +import com.pramodbharti.filmo.data.network.models.MovieResponse +import com.pramodbharti.filmo.data.network.models.MoviesResponse +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class NetworkMoviesRepository(private val moviesApiService: MoviesApiService, private val dispatcher: CoroutineDispatcher) : MoviesRepository { + override suspend fun getTrendingMovies(): MoviesResponse { + return withContext(dispatcher) { + moviesApiService.getTrendingMovies() + } + } + + override suspend fun getNowPlayingMovies(): MoviesResponse { + return withContext(dispatcher) { + moviesApiService.getNowPlayingMovies() + } + } + + override suspend fun getPopularMovies(): MoviesResponse { + return withContext(dispatcher) { + moviesApiService.getPopularMovies() + } + } + + override suspend fun getUpcomingMovies(): MoviesResponse { + return withContext(dispatcher) { + moviesApiService.getUpcomingMovies() + } + } + + override suspend fun getTopRatedMovies(): MoviesResponse { + return withContext(dispatcher) { + moviesApiService.getTopRatedMovies() + } + } + + override suspend fun getMovieDetails(movieId: Int): MovieResponse { + return withContext(dispatcher) { + moviesApiService.getMovieDetails(movieId) + } + } -class NetworkMoviesRepository(private val moviesApiService: MoviesApiService) : MoviesRepository { - override suspend fun getDiscoverMovies() = moviesApiService.getDiscoverMovies() } \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt b/app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt new file mode 100644 index 0000000..9416b59 --- /dev/null +++ b/app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt @@ -0,0 +1,42 @@ +package com.pramodbharti.filmo.domain + +import com.pramodbharti.filmo.R +import com.pramodbharti.filmo.data.network.models.MovieResponse +import com.pramodbharti.filmo.data.repositories.MoviesRepository +import com.pramodbharti.filmo.ui.models.MediaItem +import com.pramodbharti.filmo.ui.models.Movies +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope + +class MoviesUseCase(private val moviesRepository: MoviesRepository) { + suspend fun getMovies(): Movies { + return coroutineScope { + val trendingMovies = + async { moviesRepository.getTrendingMovies().results.take(7).map { it.toMovieItem() } } + val popularMovies = + async { moviesRepository.getPopularMovies().results.map { it.toMovieItem() } } + val nowPlaying = + async { moviesRepository.getNowPlayingMovies().results.map { it.toMovieItem() } } + val topRatedMovies = + async { moviesRepository.getTopRatedMovies().results.map { it.toMovieItem() } } + val upcomingMovies = + async { moviesRepository.getUpcomingMovies().results.map { it.toMovieItem() } } + Movies( + trendingMovies.await(), + nowPlaying.await(), + popularMovies.await(), + topRatedMovies.await(), + upcomingMovies.await() + ) + } + } +} + +private fun MovieResponse.toMovieItem(): MediaItem = + MediaItem( + id = id, + title = title, + poster = posterPath, + backdrop = backdropPath, + releaseDate = releaseDate + ) \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt b/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt index a6559bc..575f3e5 100644 --- a/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt +++ b/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt @@ -8,39 +8,39 @@ val dummyMovies: List = listOf( MediaItem( 123, "Testing one", - R.drawable.poster1, - R.drawable.back_drop1, + "", + "", "" ), MediaItem( 133, "Testing one", - R.drawable.poster2, - R.drawable.back_drop2, + "", + "", "" ), MediaItem( 1230, "Testing one", - R.drawable.poster1, - R.drawable.back_drop1, + "", + "", "" ), MediaItem( 123, "Testing one", - R.drawable.poster1, - R.drawable.back_drop1, + "", + "", "" ), MediaItem( 133, "Testing one", - R.drawable.poster2, - R.drawable.back_drop2, + "", + "", "" ), MediaItem( 1230, "Testing one", - R.drawable.poster1, - R.drawable.back_drop1, + "", + "", "" ) ) diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/components/CarouselItem.kt b/app/src/main/java/com/pramodbharti/filmo/ui/components/CarouselItem.kt index 6b31298..f7556e1 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/components/CarouselItem.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/components/CarouselItem.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest +import com.pramodbharti.filmo.R import com.pramodbharti.filmo.dummydata.dummyMovies import com.pramodbharti.filmo.ui.Constants import com.pramodbharti.filmo.ui.models.MediaItem @@ -50,8 +51,8 @@ fun CarouselItem( .build(), contentDescription = movieItem.title, contentScale = ContentScale.Crop, - placeholder = painterResource(id = movieItem.backdrop), - error = painterResource(id = movieItem.backdrop), + placeholder = painterResource(id = R.drawable.placeholder), + error = painterResource(id = R.drawable.placeholder), modifier = Modifier .height(230.dp) .fillMaxWidth() @@ -72,7 +73,7 @@ fun CarouselItem( .fillMaxWidth() ) { Text( - text = "John Wick 4", + text = movieItem.title, style = TextStyle( color = Color.White, fontWeight = FontWeight.Bold diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/components/MediaItemsRow.kt b/app/src/main/java/com/pramodbharti/filmo/ui/components/MediaItemsRow.kt index b41fb5f..f54748f 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/components/MediaItemsRow.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/components/MediaItemsRow.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest +import com.pramodbharti.filmo.R import com.pramodbharti.filmo.ui.Constants import com.pramodbharti.filmo.ui.models.MediaItem @@ -53,8 +54,8 @@ fun MediaPoster( .build(), contentDescription = movieItem.title, contentScale = ContentScale.Crop, - placeholder = painterResource(id = movieItem.poster), - error = painterResource(id = movieItem.poster), + placeholder = painterResource(id = R.drawable.placeholder), + error = painterResource(id = R.drawable.placeholder), modifier = Modifier.size(150.dp, 230.dp) ) } diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/models/MediaItem.kt b/app/src/main/java/com/pramodbharti/filmo/ui/models/MediaItem.kt index 295d4b1..5443139 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/models/MediaItem.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/models/MediaItem.kt @@ -5,10 +5,8 @@ import androidx.annotation.DrawableRes data class MediaItem( val id: Int, val title: String, - @DrawableRes - val poster: Int, - @DrawableRes - val backdrop: Int, + val poster: String, + val backdrop: String, val releaseDate: String ) diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/DetailsScreen.kt b/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/DetailsScreen.kt index 0edac82..5769b2d 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/DetailsScreen.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/DetailsScreen.kt @@ -73,8 +73,8 @@ fun ItemDetails(movieItem: MediaItem, modifier: Modifier = Modifier) { .data(movieItem.backdrop) .crossfade(true) .build(), - placeholder = painterResource(id = movieItem.backdrop), - error = painterResource(id = movieItem.backdrop), + placeholder = painterResource(id =R.drawable.placeholder), + error = painterResource(id = R.drawable.placeholder), contentScale = ContentScale.Crop, contentDescription = null, modifier = Modifier diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/screens/favs/FavItemsScreen.kt b/app/src/main/java/com/pramodbharti/filmo/ui/screens/favs/FavItemsScreen.kt index f91ed66..72717e8 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/screens/favs/FavItemsScreen.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/screens/favs/FavItemsScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.pramodbharti.filmo.R import com.pramodbharti.filmo.dummydata.dummyMovies import com.pramodbharti.filmo.ui.models.MediaItem import com.pramodbharti.filmo.ui.theme.FilmoTheme @@ -53,7 +54,7 @@ fun FavItem(item: MediaItem, modifier: Modifier = Modifier, onDeleteClick: (Medi Card(elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)) { Row(modifier = Modifier.padding(8.dp)) { Image( - painter = painterResource(id = item.poster), + painter = painterResource(id = R.drawable.placeholder), contentDescription = item.title, contentScale = ContentScale.FillBounds, modifier = Modifier diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesScreen.kt b/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesScreen.kt index a257d77..17fb4c1 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesScreen.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesScreen.kt @@ -1,11 +1,15 @@ package com.pramodbharti.filmo.ui.screens.movies import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.util.Log import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.pramodbharti.filmo.dummydata.dummyMovies @@ -15,38 +19,53 @@ import com.pramodbharti.filmo.ui.components.MediaItemsPosterRow import com.pramodbharti.filmo.ui.components.MediaSlots import com.pramodbharti.filmo.ui.components.carouselTransition import com.pramodbharti.filmo.ui.models.MediaItem +import com.pramodbharti.filmo.ui.models.Movies import com.pramodbharti.filmo.ui.theme.FilmoTheme @OptIn(ExperimentalFoundationApi::class) @Composable fun MoviesScreen( - movies: List = emptyList(), + fmovies: List = emptyList(), onSeeAllClick: (String) -> Unit = {}, onMediaItemClick: (MediaItem) -> Unit = {}, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + viewModel: MoviesViewModel = androidx.lifecycle.viewmodel.compose.viewModel(factory = MoviesViewModel.Factory) ) { - Column(modifier.verticalScroll(rememberScrollState())) { - FilmoCarousel(itemsCount = movies.size) { index, pagerState -> - CarouselItem( - movieItem = movies[index], - modifier = modifier.carouselTransition(index, pagerState), - onMediaItemClick = onMediaItemClick - ) + val uiState by viewModel.moviesUiState.collectAsState() + when(val state = uiState){ + is MoviesUiState.Error -> { + Log.e("TAG", "MoviesScreen: Error ${state.msg}", ) } - MediaSlots(title = "Trending", onSeeAllClick = onSeeAllClick) { - MediaItemsPosterRow(movies = movies, onMediaItemClick = onMediaItemClick) + MoviesUiState.Loading -> { + Log.e("TAG", "MoviesScreen: Loading", ) } - MediaSlots(title = "Popular", onSeeAllClick = onSeeAllClick) { - MediaItemsPosterRow(movies = movies, onMediaItemClick = onMediaItemClick) - } - MediaSlots(title = "Trending", onSeeAllClick = onSeeAllClick) { - MediaItemsPosterRow(movies = movies, onMediaItemClick = onMediaItemClick) - } - MediaSlots(title = "Popular", onSeeAllClick = onSeeAllClick) { - MediaItemsPosterRow(movies = movies, onMediaItemClick = onMediaItemClick) + is MoviesUiState.Success -> { + Log.e("TAG", "MoviesScreen: Success ${state.movies.toString()}", ) + Column(modifier.verticalScroll(rememberScrollState())) { + FilmoCarousel(itemsCount = state.movies.trending.size) { index, pagerState -> + CarouselItem( + movieItem = state.movies.trending[index], + modifier = modifier.carouselTransition(index, pagerState), + onMediaItemClick = onMediaItemClick + ) + } + MediaSlots(title = "Now Playing", onSeeAllClick = onSeeAllClick) { + MediaItemsPosterRow(movies = state.movies.nowPlaying, onMediaItemClick = onMediaItemClick) + } + MediaSlots(title = "Top Rated", onSeeAllClick = onSeeAllClick) { + MediaItemsPosterRow(movies = state.movies.topRated, onMediaItemClick = onMediaItemClick) + } + MediaSlots(title = "Popular", onSeeAllClick = onSeeAllClick) { + MediaItemsPosterRow(movies = state.movies.popular, onMediaItemClick = onMediaItemClick) + } + MediaSlots(title = "Upcoming", onSeeAllClick = onSeeAllClick) { + MediaItemsPosterRow(movies = state.movies.upcoming, onMediaItemClick = onMediaItemClick) + } + } } } + } diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesViewModel.kt b/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesViewModel.kt index ed30ad3..1531740 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesViewModel.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/screens/movies/MoviesViewModel.kt @@ -13,47 +13,45 @@ import com.pramodbharti.filmo.FilmoApplication import com.pramodbharti.filmo.R import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.repositories.MoviesRepository +import com.pramodbharti.filmo.domain.MoviesUseCase import com.pramodbharti.filmo.ui.models.MediaItem +import com.pramodbharti.filmo.ui.models.Movies +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import java.io.IOException -class MoviesViewModel(private val moviesRepository: MoviesRepository) : ViewModel() { - var movieUiState: MoviesUiState by mutableStateOf(MoviesUiState.Loading) - private set +class MoviesViewModel(private val moviesUseCase: MoviesUseCase) : ViewModel() { + private val _moviesUiState = MutableStateFlow(MoviesUiState.Loading) + val moviesUiState: StateFlow = _moviesUiState init { getMovies() } private fun getMovies() { -// viewModelScope.launch { -// movieUiState = try { -// val movieList = moviesRepository.getDiscoverMovies() -// MoviesUiState.Success(movieList.results.map { it.toMovieItem() }) -// } catch (e: IOException) { -// MoviesUiState.Error -// } -// } + viewModelScope.launch { + try { + val movies = moviesUseCase.getMovies() + _moviesUiState.value = MoviesUiState.Success(movies) + } catch (e: IOException) { + _moviesUiState.value = MoviesUiState.Error("Something went wrong! ${e.localizedMessage}") + } + + } } companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { val application = (this[APPLICATION_KEY] as FilmoApplication) - val moviesRepository = application.container.moviesRepository - MoviesViewModel(moviesRepository) + val moviesUseCase = application.container.moviesUseCase + MoviesViewModel(moviesUseCase) } } } - private fun MovieResponse.toMovieItem(): MediaItem = - MediaItem( - id = id, - title = title, - poster = R.drawable.poster1, - backdrop = R.drawable.back_drop1, - releaseDate = releaseDate - ) }