From 8734ea7b65eb5fa3d1fd22022c193d29f76f3e75 Mon Sep 17 00:00:00 2001 From: Pramod Bharti Date: Thu, 19 Oct 2023 00:22:57 +0530 Subject: [PATCH] casts integration --- .../filmo/data/network/MoviesApiService.kt | 10 ++++--- .../filmo/data/network/models/CastResponse.kt | 2 +- .../data/network/models/CreditsResponse.kt | 8 +++++ .../data/network/models/MovieResponse.kt | 4 +-- .../data/repositories/MoviesRepository.kt | 2 ++ .../repositories/NetworkMoviesRepository.kt | 7 +++++ .../filmo/domain/MoviesUseCase.kt | 16 ++++++++-- .../pramodbharti/filmo/dummydata/DummyData.kt | 23 ++++++++------- .../filmo/ui/components/CastItemsRow.kt | 13 +++++---- .../com/pramodbharti/filmo/ui/models/Cast.kt | 3 +- .../pramodbharti/filmo/ui/models/MediaItem.kt | 1 + .../filmo/ui/navigation/FilmoDestinations.kt | 7 +++++ .../filmo/ui/navigation/FilmoNavHost.kt | 20 ++++++++++--- .../filmo/ui/screens/details/DetailsScreen.kt | 29 +++++++++++-------- .../screens/details/MovieDetailsViewModel.kt | 26 +++++++++++++---- 15 files changed, 121 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/com/pramodbharti/filmo/data/network/models/CreditsResponse.kt 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 f82fbaa..7af3d93 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 @@ -2,6 +2,7 @@ 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.CreditsResponse import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.network.models.MoviesResponse import retrofit2.http.GET @@ -38,16 +39,17 @@ interface MoviesApiService { @GET(MOVIE_DETAILS) suspend fun getMovieDetails(@Path("movie_id") movieId: Int): MovieResponse - + @GET(CREDITS_CAST) + suspend fun getCredits(@Path("movie_id") movieId: Int): CreditsResponse 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 RECOMMENDED_MOVIES = "" - const val SIMILAR_MOVIES = "" - + const val RECOMMENDED_MOVIES = "movie/{movie_id}/recommendations" + const val SIMILAR_MOVIES = "movie/{movie_id}/similar" const val MOVIE_DETAILS = "movie/{movie_id}" + const val CREDITS_CAST = "movie/{movie_id}/credits" } } \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/data/network/models/CastResponse.kt b/app/src/main/java/com/pramodbharti/filmo/data/network/models/CastResponse.kt index 951d9a3..07129a7 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/network/models/CastResponse.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/network/models/CastResponse.kt @@ -9,6 +9,6 @@ data class CastResponse( val name: String, val gender: Int, @SerialName("profile_path") - val profilePath: String, + val profilePath: String? = null, val character: String ) diff --git a/app/src/main/java/com/pramodbharti/filmo/data/network/models/CreditsResponse.kt b/app/src/main/java/com/pramodbharti/filmo/data/network/models/CreditsResponse.kt new file mode 100644 index 0000000..c0ece61 --- /dev/null +++ b/app/src/main/java/com/pramodbharti/filmo/data/network/models/CreditsResponse.kt @@ -0,0 +1,8 @@ +package com.pramodbharti.filmo.data.network.models + +import kotlinx.serialization.Serializable + +@Serializable +data class CreditsResponse( + val cast:List +) diff --git a/app/src/main/java/com/pramodbharti/filmo/data/network/models/MovieResponse.kt b/app/src/main/java/com/pramodbharti/filmo/data/network/models/MovieResponse.kt index d690988..6985385 100644 --- a/app/src/main/java/com/pramodbharti/filmo/data/network/models/MovieResponse.kt +++ b/app/src/main/java/com/pramodbharti/filmo/data/network/models/MovieResponse.kt @@ -8,7 +8,7 @@ data class MovieResponse( val id: Int, val adult: Boolean, @SerialName("backdrop_path") - val backdropPath: String, + val backdropPath: String? = null, @SerialName("original_language") val originalLanguage: String, @SerialName("original_title") @@ -16,7 +16,7 @@ data class MovieResponse( val overview: String, val popularity: String, @SerialName("poster_path") - val posterPath: String, + val posterPath: String? = null, @SerialName("release_date") val releaseDate: String, val title: String, 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 fa6625f..1182759 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,5 +1,6 @@ package com.pramodbharti.filmo.data.repositories +import com.pramodbharti.filmo.data.network.models.CreditsResponse import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.network.models.MoviesResponse @@ -12,4 +13,5 @@ interface MoviesRepository { suspend fun getMovieDetails(movieId: Int): MovieResponse suspend fun getRecommendedMovies(movieId: Int):MoviesResponse suspend fun getSimilarMovies(movieId: Int):MoviesResponse + suspend fun getAllCast(movieId: Int):CreditsResponse } \ 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 56902a9..9b19d6f 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,6 +1,7 @@ package com.pramodbharti.filmo.data.repositories import com.pramodbharti.filmo.data.network.MoviesApiService +import com.pramodbharti.filmo.data.network.models.CreditsResponse import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.network.models.MoviesResponse import kotlinx.coroutines.CoroutineDispatcher @@ -56,4 +57,10 @@ class NetworkMoviesRepository(private val moviesApiService: MoviesApiService, pr } } + override suspend fun getAllCast(movieId: Int): CreditsResponse { + return withContext(dispatcher){ + moviesApiService.getCredits(movieId) + } + } + } \ 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 index bf0c788..b6df011 100644 --- a/app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt +++ b/app/src/main/java/com/pramodbharti/filmo/domain/MoviesUseCase.kt @@ -1,8 +1,10 @@ package com.pramodbharti.filmo.domain import com.pramodbharti.filmo.R +import com.pramodbharti.filmo.data.network.models.CastResponse import com.pramodbharti.filmo.data.network.models.MovieResponse import com.pramodbharti.filmo.data.repositories.MoviesRepository +import com.pramodbharti.filmo.ui.models.Cast import com.pramodbharti.filmo.ui.models.MediaItem import com.pramodbharti.filmo.ui.models.MovieDetails import com.pramodbharti.filmo.ui.models.Movies @@ -37,12 +39,13 @@ class MoviesUseCase(private val moviesRepository: MoviesRepository) { suspend fun getMovieDetails(movieId: Int): MovieDetails { return coroutineScope { val movie = async { moviesRepository.getMovieDetails(movieId).toMovieItem() } + val casts = async { moviesRepository.getAllCast(movieId).cast.map { it.toCast() } } val similar = async { moviesRepository.getSimilarMovies(movieId).results.map { it.toMovieItem() } } val recommended = async { moviesRepository.getRecommendedMovies(movieId).results.map { it.toMovieItem() } } - MovieDetails(movie.await(), emptyList(), similar.await(), recommended.await()) + MovieDetails(movie.await(), casts.await(), similar.await(), recommended.await()) } } } @@ -51,7 +54,14 @@ private fun MovieResponse.toMovieItem(): MediaItem = MediaItem( id = id, title = title, - poster = posterPath, - backdrop = backdropPath, + poster = posterPath ?: "", + backdrop = backdropPath ?: "", + overview = overview, releaseDate = releaseDate + ) + +private fun CastResponse.toCast(): Cast = + Cast( + name = name, + photo = profilePath?:"" ) \ 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 bd8c8f7..40ebc02 100644 --- a/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt +++ b/app/src/main/java/com/pramodbharti/filmo/dummydata/DummyData.kt @@ -10,38 +10,39 @@ val dummyMovies: List = listOf( "Testing one", "", "", + "", "" ), MediaItem( 133, "Testing one", "", "", - "" + "", "" ), MediaItem( 1230, "Testing one", "", "", - "" + "", "" ), MediaItem( 123, "Testing one", "", "", - "" + "", "" ), MediaItem( 133, "Testing one", "", "", - "" + "", "" ), MediaItem( 1230, "Testing one", "", "", - "" + "", "" ) ) @@ -57,10 +58,10 @@ val dummyGenreList = listOf( ) val dummyCastData = listOf( - Cast("Pramod Bharti", R.drawable.dddd), - Cast("Konark Chakra", R.drawable.placeholder), - Cast("Unknown Profile", R.drawable.profile_picture), - Cast("Pramod Bharti", R.drawable.dddd), - Cast("Konark Chakra", R.drawable.placeholder), - Cast("Unknown Profile", R.drawable.profile_picture) + Cast("Pramod Bharti", ""), + Cast("Konark Chakra", ""), + Cast("Unknown Profile", ""), + Cast("Pramod Bharti", ""), + Cast("Konark Chakra", ""), + Cast("Unknown Profile", "") ) \ No newline at end of file diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/components/CastItemsRow.kt b/app/src/main/java/com/pramodbharti/filmo/ui/components/CastItemsRow.kt index aa4d31d..777d1e4 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/components/CastItemsRow.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/components/CastItemsRow.kt @@ -21,6 +21,9 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest +import coil.size.Scale +import com.pramodbharti.filmo.R +import com.pramodbharti.filmo.ui.Constants import com.pramodbharti.filmo.ui.models.Cast @Composable @@ -45,20 +48,20 @@ fun CastItem(cast: Cast, modifier: Modifier = Modifier) { AsyncImage( model = ImageRequest .Builder(context = LocalContext.current) - .data(cast.photo) + .data("${Constants.IMAGE_URL_500}${cast.photo}") .crossfade(true) .build(), - placeholder = painterResource(id = cast.photo), - error = painterResource(id = cast.photo), + placeholder = painterResource(id = R.drawable.placeholder), + error = painterResource(id = R.drawable.placeholder), contentScale = ContentScale.Crop, - contentDescription = null, + contentDescription = cast.name, modifier = Modifier .clip(CircleShape) .height(80.dp) ) Text( text = cast.name, - style = MaterialTheme.typography.bodyMedium, + style = MaterialTheme.typography.bodySmall, textAlign = TextAlign.Center ) } diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/models/Cast.kt b/app/src/main/java/com/pramodbharti/filmo/ui/models/Cast.kt index 0bc4a3c..62838a2 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/models/Cast.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/models/Cast.kt @@ -4,6 +4,5 @@ import androidx.annotation.DrawableRes data class Cast( val name: String, - @DrawableRes - val photo: Int + val photo: String ) 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 5443139..0dfc253 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 @@ -7,6 +7,7 @@ data class MediaItem( val title: String, val poster: String, val backdrop: String, + val overview: String, val releaseDate: String ) diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoDestinations.kt b/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoDestinations.kt index ebdd5d0..6ca3b51 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoDestinations.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoDestinations.kt @@ -4,6 +4,8 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowLeft import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector +import androidx.navigation.NavType +import androidx.navigation.navArgument import com.pramodbharti.filmo.ui.screens.details.DetailsScreen import com.pramodbharti.filmo.ui.screens.favs.FavItemsScreen import com.pramodbharti.filmo.ui.screens.movies.MoviesScreen @@ -33,6 +35,11 @@ object Favs : FilmoDestination { object Details : FilmoDestination { override val icon = Icons.Filled.KeyboardArrowLeft override val route = "details" + val movieId = "movie_id" + val routeWithArgs = "$route/{$movieId}" + val arguments = listOf( + navArgument(movieId) { type = NavType.IntType } + ) } object SeeAll : FilmoDestination { diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoNavHost.kt b/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoNavHost.kt index 13d556f..0b69088 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoNavHost.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/navigation/FilmoNavHost.kt @@ -1,5 +1,6 @@ package com.pramodbharti.filmo.ui.navigation +import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavGraph.Companion.findStartDestination @@ -24,7 +25,10 @@ fun FilmoNavHost( modifier = modifier ) { composable(Movies.route) { - MoviesScreen() + MoviesScreen(onMediaItemClick = { item -> + Log.e("TAG", "FilmoNavHost: ${item.toString()}") + navController.navigateToDetailsScreen(item.id) + }) } composable(TvShows.route) { @@ -39,8 +43,12 @@ fun FilmoNavHost( SeeAllScreen() } - composable(Details.route) { - DetailsScreen() + composable( + route = Details.routeWithArgs, + arguments = Details.arguments + ) { navBackStack -> + val movieId = navBackStack.arguments?.getInt(Details.movieId) + DetailsScreen(movieId = movieId) } } } @@ -50,4 +58,8 @@ fun NavHostController.navigateSingleTopTo(route: String) = popUpTo(this@navigateSingleTopTo.graph.findStartDestination().id) { saveState = true } launchSingleTop = true restoreState = true - } \ No newline at end of file + } + +fun NavHostController.navigateToDetailsScreen(movieId: Int) { + this.navigateSingleTopTo("${Details.route}/$movieId") +} \ No newline at end of file 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 fe68b0c..e11a6d1 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 @@ -1,5 +1,6 @@ package com.pramodbharti.filmo.ui.screens.details +import android.service.autofill.UserData import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -35,6 +36,7 @@ import com.pramodbharti.filmo.R import com.pramodbharti.filmo.dummydata.dummyCastData import com.pramodbharti.filmo.dummydata.dummyGenreList import com.pramodbharti.filmo.dummydata.dummyMovies +import com.pramodbharti.filmo.ui.Constants import com.pramodbharti.filmo.ui.components.CastItemsRow import com.pramodbharti.filmo.ui.components.CastSlots import com.pramodbharti.filmo.ui.components.MediaItemsPosterRow @@ -47,23 +49,28 @@ import com.pramodbharti.filmo.ui.theme.FilmoTheme fun DetailsScreen( modifier: Modifier = Modifier, movieItem: MediaItem? = null, + movieId: Int? = null, viewModel: MovieDetailsViewModel = viewModel(factory = MovieDetailsViewModel.Factory) ) { val movieDetailsUiState by viewModel.movieDetailsUiState.collectAsState() when (val state = movieDetailsUiState) { - is MovieDetailUiState.Error -> TODO() - MovieDetailUiState.Loading -> TODO() + is MovieDetailUiState.Error -> {} + MovieDetailUiState.Loading -> {} is MovieDetailUiState.Success -> { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { ItemDetails(movieItem = state.movies.movie) CastSlots(title = "Cast") { CastItemsRow(casts = state.movies.casts) } - MediaSlots(title = "Similar") { - MediaItemsPosterRow(movies = state.movies.similarMovies) + if (state.movies.similarMovies.isNotEmpty()) { + MediaSlots(title = "Similar") { + MediaItemsPosterRow(movies = state.movies.similarMovies) + } } - MediaSlots(title = "Recommended for you") { - MediaItemsPosterRow(movies = state.movies.recommendedMovies) + if (state.movies.recommendedMovies.isNotEmpty()) { + MediaSlots(title = "Recommended for you") { + MediaItemsPosterRow(movies = state.movies.recommendedMovies) + } } } } @@ -85,7 +92,7 @@ fun ItemDetails(movieItem: MediaItem, modifier: Modifier = Modifier) { AsyncImage( model = ImageRequest .Builder(context = LocalContext.current) - .data(movieItem.backdrop) + .data("${Constants.IMAGE_URL_500}${movieItem.poster}") .crossfade(true) .build(), placeholder = painterResource(id = R.drawable.placeholder), @@ -120,7 +127,7 @@ fun ItemDetailsSection(movieItem: MediaItem, modifier: Modifier = Modifier) { ) { TitleAndFavorite(title = movieItem.title) TagItemsRow(tags = dummyGenreList) - AboutSection() + AboutSection(overview = movieItem.overview) } } @@ -163,11 +170,9 @@ fun TitleAndFavorite(title: String, modifier: Modifier = Modifier) { } @Composable -fun AboutSection(modifier: Modifier = Modifier) { +fun AboutSection(overview: String, modifier: Modifier = Modifier) { Text( - text = "How to scroll view pager (accompanist library) on button click in jetpack compose Android".repeat( - 3 - ), + text = overview, style = MaterialTheme.typography.titleMedium, color = Color.LightGray, fontSize = 10.sp, diff --git a/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/MovieDetailsViewModel.kt b/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/MovieDetailsViewModel.kt index ca06e3b..369ca7b 100644 --- a/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/MovieDetailsViewModel.kt +++ b/app/src/main/java/com/pramodbharti/filmo/ui/screens/details/MovieDetailsViewModel.kt @@ -1,12 +1,16 @@ package com.pramodbharti.filmo.ui.screens.details +import android.util.Log +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import com.pramodbharti.filmo.FilmoApplication import com.pramodbharti.filmo.domain.MoviesUseCase +import com.pramodbharti.filmo.ui.navigation.Details import com.pramodbharti.filmo.ui.screens.movies.MoviesUiState import com.pramodbharti.filmo.ui.screens.movies.MoviesViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -14,17 +18,26 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import java.io.IOException -class MovieDetailsViewModel(private val moviesUseCase: MoviesUseCase) : ViewModel() { - private val _movieDetailsUiState = MutableStateFlow(MovieDetailUiState.Loading) +class MovieDetailsViewModel( + savedStateHandle: SavedStateHandle, + private val moviesUseCase: MoviesUseCase +) : ViewModel() { + private val _movieDetailsUiState = + MutableStateFlow(MovieDetailUiState.Loading) val movieDetailsUiState: StateFlow = _movieDetailsUiState + init { - getMovies() + val movieId = savedStateHandle.get(Details.movieId) + Log.e("TAG", ": movieIddd: $movieId") + movieId?.let { + getMovieDetails(movieId) + } } - private fun getMovies() { + private fun getMovieDetails(movieId: Int) { viewModelScope.launch { try { - val movieDetail = moviesUseCase.getMovieDetails(67) + val movieDetail = moviesUseCase.getMovieDetails(movieId) _movieDetailsUiState.value = MovieDetailUiState.Success(movieDetail) } catch (e: IOException) { _movieDetailsUiState.value = @@ -33,13 +46,14 @@ class MovieDetailsViewModel(private val moviesUseCase: MoviesUseCase) : ViewMode } } + companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { val application = (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as FilmoApplication) val moviesUseCase = application.container.moviesUseCase - MoviesViewModel(moviesUseCase) + MovieDetailsViewModel(createSavedStateHandle(), moviesUseCase = moviesUseCase) } } }