From 86034bf19872b4fff5e1273850d1153aae8c9c00 Mon Sep 17 00:00:00 2001 From: Owen LeJeune Date: Tue, 15 Feb 2022 11:07:13 -0500 Subject: [PATCH] display all backdrops as scrolling view --- app/build.gradle | 1 + .../com/owenlejeune/tvtime/MainActivity.kt | 4 +- .../tvtime/api/tmdb/DetailService.kt | 5 +- .../owenlejeune/tvtime/api/tmdb/MoviesApi.kt | 5 +- .../tvtime/api/tmdb/MoviesService.kt | 9 ++- .../owenlejeune/tvtime/api/tmdb/TmdbUtils.kt | 9 +++ .../com/owenlejeune/tvtime/api/tmdb/TvApi.kt | 6 +- .../owenlejeune/tvtime/api/tmdb/TvService.kt | 9 ++- .../tvtime/api/tmdb/model/Image.kt | 9 +++ .../tvtime/api/tmdb/model/ImageCollection.kt | 8 +++ .../tvtime/ui/components/Posters.kt | 63 ++++++++++++++----- .../tvtime/ui/screens/DetailView.kt | 29 +++++++-- 12 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Image.kt create mode 100644 app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/ImageCollection.kt diff --git a/app/build.gradle b/app/build.gradle index 44d8cfb..8d91d88 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,7 @@ dependencies { implementation "androidx.compose.ui:ui-tooling-preview:${Versions.compose}" implementation "androidx.activity:activity-compose:${Versions.activity_compose}" implementation "com.google.accompanist:accompanist-systemuicontroller:${Versions.compose_accompanist}" + implementation "com.google.accompanist:accompanist-pager:${Versions.compose_accompanist}" implementation "androidx.navigation:navigation-compose:${Versions.compose_navigation}" implementation "androidx.paging:paging-compose:${Versions.compose_paging}" implementation "androidx.constraintlayout:constraintlayout-compose:${Versions.compose_constraint_layout}" diff --git a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt index bf0bae0..6e1dc0e 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt @@ -31,9 +31,7 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { val displayUnderStatusBar = remember { mutableStateOf(false) } -// if (displayUnderStatusBar.value) { - WindowCompat.setDecorFitsSystemWindows(window, !displayUnderStatusBar.value) -// } + WindowCompat.setDecorFitsSystemWindows(window, !displayUnderStatusBar.value) MyApp(displayUnderStatusBar = displayUnderStatusBar) } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt index bb5c783..9c8c652 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/DetailService.kt @@ -1,10 +1,13 @@ package com.owenlejeune.tvtime.api.tmdb +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem import retrofit2.Response interface DetailService { - suspend fun getById(id: Int): Response + suspend fun getById(id: Int): Response + + suspend fun getImages(id: Int): Response } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt index 0d71921..cc3a30f 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesApi.kt @@ -1,7 +1,7 @@ package com.owenlejeune.tvtime.api.tmdb +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.DetailedMovie -import com.owenlejeune.tvtime.api.tmdb.model.PopularMovie import com.owenlejeune.tvtime.api.tmdb.model.PopularMoviesResponse import retrofit2.Response import retrofit2.http.GET @@ -16,4 +16,7 @@ interface MoviesApi { @GET("movie/{id}") suspend fun getMovieById(@Path("id") id: Int): Response + @GET("movie/{id}/images") + suspend fun getMovieImages(@Path("id") id: Int): Response + } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt index 07a094a..ff34c8b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/MoviesService.kt @@ -1,5 +1,6 @@ package com.owenlejeune.tvtime.api.tmdb +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem import org.koin.core.component.KoinComponent import retrofit2.Response @@ -10,6 +11,12 @@ class MoviesService: KoinComponent, DetailService { suspend fun getPopularMovies(page: Int = 1) = service.getPopularMovies(page) - override suspend fun getById(id: Int): Response = service.getMovieById(id) as Response + override suspend fun getById(id: Int): Response { + return service.getMovieById(id) + } + + override suspend fun getImages(id: Int): Response { + return service.getMovieImages(id) + } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbUtils.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbUtils.kt index c4170f2..02d6acc 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbUtils.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbUtils.kt @@ -1,6 +1,7 @@ package com.owenlejeune.tvtime.api.tmdb import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem +import com.owenlejeune.tvtime.api.tmdb.model.Image import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem object TmdbUtils { @@ -13,6 +14,10 @@ object TmdbUtils { return tmdbItem?.let { getFullPosterPath(tmdbItem.posterPath) } } + fun getFullPosterPath(image: Image): String? { + return getFullPosterPath(image.filePath) + } + fun getFullBackdropPath(backdropPath: String?): String? { return backdropPath?.let { "https://www.themoviedb.org/t/p/original${backdropPath}" } } @@ -21,4 +26,8 @@ object TmdbUtils { return detailItem?.let { getFullBackdropPath(detailItem.backdropPath) } } + fun getFullBackdropPath(image: Image): String? { + return getFullBackdropPath(image.filePath) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt index ba4a9c4..613008a 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvApi.kt @@ -1,5 +1,6 @@ package com.owenlejeune.tvtime.api.tmdb +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.PopularTvResponse import com.owenlejeune.tvtime.api.tmdb.model.DetailedTv import retrofit2.Response @@ -13,6 +14,9 @@ interface TvApi { suspend fun getPoplarTv(@Query("page") page: Int = 1): Response @GET("tv/{id}") - suspend fun getTvShowById(@Path("id") id: Int): Response + suspend fun getTvShowById(@Path("id") id: Int): Response + + @GET("tv/{id}/images") + suspend fun getTvImages(@Path("id") id: Int): Response } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt index b500b52..12035c7 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TvService.kt @@ -1,5 +1,6 @@ package com.owenlejeune.tvtime.api.tmdb +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem import org.koin.core.component.KoinComponent import retrofit2.Response @@ -10,7 +11,11 @@ class TvService: KoinComponent, DetailService { suspend fun getPopularTv(page: Int = 1) = service.getPoplarTv(page) - override suspend fun getById(id: Int): Response { - return service.getTvShowById(id) as Response + override suspend fun getById(id: Int): Response { + return service.getTvShowById(id) + } + + override suspend fun getImages(id: Int): Response { + return service.getTvImages(id) } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Image.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Image.kt new file mode 100644 index 0000000..bf7b9d6 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/Image.kt @@ -0,0 +1,9 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +data class Image( + @SerializedName("file_path") val filePath: String, + @SerializedName("height") val height: Int, + @SerializedName("width") val width: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/ImageCollection.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/ImageCollection.kt new file mode 100644 index 0000000..f811808 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/model/ImageCollection.kt @@ -0,0 +1,8 @@ +package com.owenlejeune.tvtime.api.tmdb.model + +import com.google.gson.annotations.SerializedName + +data class ImageCollection( + @SerializedName("backdrops") val backdrops: List, + @SerializedName("posters") val posters: List +) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt index 416c6c4..208e955 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt @@ -1,5 +1,6 @@ package com.owenlejeune.tvtime.ui.components +import android.annotation.SuppressLint import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.GridCells import androidx.compose.foundation.lazy.LazyVerticalGrid +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush @@ -22,11 +24,16 @@ import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import coil.compose.rememberImagePainter import coil.transform.RoundedCornersTransformation +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.HorizontalPager +import com.google.accompanist.pager.rememberPagerState import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.TmdbUtils +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.api.tmdb.model.TmdbItem import com.owenlejeune.tvtime.extensions.dpToPx import com.owenlejeune.tvtime.extensions.listItems +import kotlinx.coroutines.* @OptIn(ExperimentalFoundationApi::class) @Composable @@ -84,17 +91,20 @@ fun PosterItem( ) } +@SuppressLint("CoroutineCreationDuringComposition") +@OptIn(ExperimentalPagerApi::class) @Composable fun BackdropImage( modifier: Modifier = Modifier, - imageUrl: String? + imageUrl: String? = null, + collection: ImageCollection? = null ) { val context = LocalContext.current var sizeImage by remember { mutableStateOf(IntSize.Zero) } val gradient = Brush.verticalGradient( - colors = listOf(Color.Transparent, Color.Black), + colors = listOf(Color.Transparent, MaterialTheme.colorScheme.background), startY = sizeImage.height.toFloat() / 3, endY = sizeImage.height.toFloat() ) @@ -102,24 +112,45 @@ fun BackdropImage( Box( modifier = modifier ) { - Image( - painter = if (imageUrl != null) { - rememberImagePainter( - data = imageUrl, - builder = { - placeholder(R.drawable.placeholder) + if (collection != null) { + val pagerState = rememberPagerState() + HorizontalPager(count = collection.backdrops.size, state = pagerState) { page -> + val backdrop = collection.backdrops[page] + Image( + painter = rememberImagePainter( + data = TmdbUtils.getFullBackdropPath(backdrop), + builder = { + placeholder(R.drawable.placeholder) + } + ), + contentDescription = "", + modifier = Modifier.onGloballyPositioned { + sizeImage = it.size } ) - } else { - rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder)) - }, - contentDescription = "", - modifier = Modifier.onGloballyPositioned { - sizeImage = it.size } - ) + } else { + Image( + painter = if (imageUrl != null) { + rememberImagePainter( + data = imageUrl, + builder = { + placeholder(R.drawable.placeholder) + } + ) + } else { + rememberImagePainter(ContextCompat.getDrawable(context, R.drawable.placeholder)) + }, + contentDescription = "", + modifier = Modifier.onGloballyPositioned { + sizeImage = it.size + } + ) + } Box( - modifier = Modifier.matchParentSize().background(gradient) + modifier = Modifier + .matchParentSize() + .background(gradient) ) } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt index 64aecc7..ca99422 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/DetailView.kt @@ -25,6 +25,8 @@ import com.owenlejeune.tvtime.api.tmdb.MoviesService import com.owenlejeune.tvtime.api.tmdb.TmdbUtils import com.owenlejeune.tvtime.api.tmdb.TvService import com.owenlejeune.tvtime.api.tmdb.model.DetailedItem +import com.owenlejeune.tvtime.api.tmdb.model.Image +import com.owenlejeune.tvtime.api.tmdb.model.ImageCollection import com.owenlejeune.tvtime.ui.components.BackdropImage import com.owenlejeune.tvtime.ui.components.PosterItem import kotlinx.coroutines.CoroutineScope @@ -39,16 +41,21 @@ fun DetailView( type: DetailViewType ) { val context = LocalContext.current - - val mediaItem = remember { mutableStateOf(null) } val service = when(type) { DetailViewType.MOVIE -> MoviesService() DetailViewType.TV -> TvService() } + + val mediaItem = remember { mutableStateOf(null) } itemId?.let { fetchMediaItem(itemId, service, mediaItem) } + val images = remember { mutableStateOf(null) } + itemId?.let { + fetchImages(itemId, service, images) + } + ConstraintLayout( modifier = Modifier .fillMaxSize() @@ -69,7 +76,8 @@ fun DetailView( } .fillMaxWidth() .size(0.dp, 280.dp), - imageUrl = TmdbUtils.getFullBackdropPath(mediaItem.value) + imageUrl = TmdbUtils.getFullBackdropPath(mediaItem.value), + collection = images.value ) PosterItem( @@ -78,7 +86,7 @@ fun DetailView( .constrainAs(posterImage) { bottom.linkTo(title.top, margin = 8.dp) start.linkTo(parent.start, margin = 16.dp) - top.linkTo(backButton.bottom) + top.linkTo(backButton.bottom, margin = 8.dp) } ) @@ -122,7 +130,18 @@ private fun fetchMediaItem(id: Int, service: DetailService, mediaItem: MutableSt val response = service.getById(id) if (response.isSuccessful) { withContext(Dispatchers.Main) { - mediaItem.value = response.body()!! + mediaItem.value = response.body() + } + } + } +} + +private fun fetchImages(id: Int, service: DetailService, images: MutableState) { + CoroutineScope(Dispatchers.IO).launch { + val response = service.getImages(id) + if (response.isSuccessful) { + withContext(Dispatchers.Main) { + images.value = response.body() } } }