Skip to content

Commit

Permalink
Pipe new entity class for images through
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbanes committed Jun 10, 2019
1 parent 539cb6a commit 7c16f44
Show file tree
Hide file tree
Showing 39 changed files with 247 additions and 146 deletions.
26 changes: 26 additions & 0 deletions app/src/main/java/app/tivi/extensions/GlideKtExtensions.kt
@@ -0,0 +1,26 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package app.tivi.extensions

import android.graphics.drawable.Drawable
import app.tivi.ui.glide.GlideRequest
import app.tivi.ui.glide.GlideRequests

fun GlideRequests.optSaturateOnLoad(saturateOnLoad: Boolean): GlideRequest<Drawable> = when {
saturateOnLoad -> saturateOnLoad()
else -> asDrawable()
}
Expand Up @@ -19,6 +19,7 @@ package app.tivi.home.discover
import app.tivi.HeaderBindingModel_ import app.tivi.HeaderBindingModel_
import app.tivi.R import app.tivi.R
import app.tivi.data.Entry import app.tivi.data.Entry
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.EntryWithShow import app.tivi.data.resultentities.EntryWithShow
import app.tivi.data.resultentities.PopularEntryWithShow import app.tivi.data.resultentities.PopularEntryWithShow
import app.tivi.data.resultentities.TrendingEntryWithShow import app.tivi.data.resultentities.TrendingEntryWithShow
Expand Down Expand Up @@ -58,7 +59,8 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
id(item.generateStableId()) id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider) tmdbImageUrlProvider(tmdbImageUrlProvider)
tiviShow(item.show) tiviShow(item.show)
annotationLabel(item.entry?.watchers.toString()) posterImage(item.images.findHighestRatedPoster())
annotationLabel(item.entry.watchers.toString())
annotationIcon(R.drawable.ic_eye_12dp) annotationIcon(R.drawable.ic_eye_12dp)
transitionName("trending_${item.show.homepage}") transitionName("trending_${item.show.homepage}")
clickListener { model, _, _, _ -> clickListener { model, _, _, _ ->
Expand Down Expand Up @@ -86,6 +88,7 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
posterGridItem { posterGridItem {
id(item.generateStableId()) id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider) tmdbImageUrlProvider(tmdbImageUrlProvider)
posterImage(item.images.findHighestRatedPoster())
tiviShow(item.show) tiviShow(item.show)
transitionName("popular_${item.show.homepage}") transitionName("popular_${item.show.homepage}")
clickListener { model, _, _, _ -> clickListener { model, _, _, _ ->
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/app/tivi/home/discover/DiscoverViewModel.kt
Expand Up @@ -49,10 +49,12 @@ class DiscoverViewModel @AssistedInject constructor(
.execute { copy(isLoading = it() ?: false) } .execute { copy(isLoading = it() ?: false) }


observeTrendingShows.observe() observeTrendingShows.observe()
.distinctUntilChanged()
.execute { copy(trendingItems = it() ?: emptyList()) } .execute { copy(trendingItems = it() ?: emptyList()) }
observeTrendingShows(Unit) observeTrendingShows(Unit)


observePopularShows.observe() observePopularShows.observe()
.distinctUntilChanged()
.execute { copy(popularItems = it() ?: emptyList()) } .execute { copy(popularItems = it() ?: emptyList()) }
observePopularShows(Unit) observePopularShows(Unit)


Expand Down
Expand Up @@ -24,6 +24,7 @@ import android.view.View
import app.tivi.HeaderBindingModel_ import app.tivi.HeaderBindingModel_
import app.tivi.LibraryFollowedItemBindingModel_ import app.tivi.LibraryFollowedItemBindingModel_
import app.tivi.data.entities.SortOption import app.tivi.data.entities.SortOption
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.FollowedShowEntryWithShow import app.tivi.data.resultentities.FollowedShowEntryWithShow
import app.tivi.emptyState import app.tivi.emptyState
import app.tivi.filter import app.tivi.filter
Expand Down Expand Up @@ -88,6 +89,7 @@ class FollowedEpoxyController @Inject constructor(
if (item != null) { if (item != null) {
id(item.generateStableId()) id(item.generateStableId())
tiviShow(item.show) tiviShow(item.show)
posterImage(item.images.findHighestRatedPoster())
posterTransitionName("show_${item.show.homepage}") posterTransitionName("show_${item.show.homepage}")
clickListener(View.OnClickListener { clickListener(View.OnClickListener {
callbacks?.onItemClicked(item) callbacks?.onItemClicked(item)
Expand Down
Expand Up @@ -30,7 +30,7 @@ class TrendingShowsFragment : EntryGridFragment<TrendingEntryWithShow, TrendingS
return object : EntryGridEpoxyController<TrendingEntryWithShow>(R.string.discover_trending) { return object : EntryGridEpoxyController<TrendingEntryWithShow>(R.string.discover_trending) {
override fun buildItemModel(item: TrendingEntryWithShow): PosterGridItemBindingModel_ { override fun buildItemModel(item: TrendingEntryWithShow): PosterGridItemBindingModel_ {
return super.buildItemModel(item) return super.buildItemModel(item)
.annotationLabel(item.entry?.watchers.toString()) .annotationLabel(item.entry.watchers.toString())
.annotationIcon(R.drawable.ic_eye_12dp) .annotationIcon(R.drawable.ic_eye_12dp)
} }
} }
Expand Down
Expand Up @@ -24,6 +24,7 @@ import android.view.View
import app.tivi.HeaderBindingModel_ import app.tivi.HeaderBindingModel_
import app.tivi.LibraryWatchedItemBindingModel_ import app.tivi.LibraryWatchedItemBindingModel_
import app.tivi.data.entities.SortOption import app.tivi.data.entities.SortOption
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.WatchedShowEntryWithShow import app.tivi.data.resultentities.WatchedShowEntryWithShow
import app.tivi.emptyState import app.tivi.emptyState
import app.tivi.filter import app.tivi.filter
Expand Down Expand Up @@ -90,6 +91,7 @@ class WatchedEpoxyController @Inject constructor(
if (item != null) { if (item != null) {
id(item.generateStableId()) id(item.generateStableId())
tiviShow(item.show) tiviShow(item.show)
posterImage(item.images.findHighestRatedPoster())
posterTransitionName("show_${item.show.homepage}") posterTransitionName("show_${item.show.homepage}")
clickListener(View.OnClickListener { clickListener(View.OnClickListener {
callbacks?.onItemClicked(item) callbacks?.onItemClicked(item)
Expand Down
Expand Up @@ -26,6 +26,7 @@ import app.tivi.data.entities.ActionDate
import app.tivi.data.entities.Episode import app.tivi.data.entities.Episode
import app.tivi.data.entities.Season import app.tivi.data.entities.Season
import app.tivi.data.entities.TiviShow import app.tivi.data.entities.TiviShow
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.RelatedShowEntryWithShow import app.tivi.data.resultentities.RelatedShowEntryWithShow
import app.tivi.data.resultentities.SeasonWithEpisodesAndWatches import app.tivi.data.resultentities.SeasonWithEpisodesAndWatches
import app.tivi.databinding.ViewHolderDetailsSeasonBinding import app.tivi.databinding.ViewHolderDetailsSeasonBinding
Expand Down Expand Up @@ -118,34 +119,31 @@ class ShowDetailsEpoxyController @Inject constructor(
relatedShows: Async<List<RelatedShowEntryWithShow>>, relatedShows: Async<List<RelatedShowEntryWithShow>>,
tmdbImageUrlProvider: Async<TmdbImageUrlProvider> tmdbImageUrlProvider: Async<TmdbImageUrlProvider>
) { ) {
when (relatedShows) { if (relatedShows is Success) {
is Success -> { val related = relatedShows()
val related = relatedShows() if (related.isNotEmpty()) {
if (related.isNotEmpty()) { detailsHeader {
detailsHeader { id("related_header")
id("related_header") title(R.string.details_related)
title(R.string.details_related) spanSizeOverride(TotalSpanOverride)
spanSizeOverride(TotalSpanOverride) }
} carousel {
carousel { id("related_shows")
id("related_shows") numViewsToShowOnScreen(5.25f)
numViewsToShowOnScreen(5.25f) hasFixedSize(true)
hasFixedSize(true)

val small = context.resources.getDimensionPixelSize(R.dimen.spacing_small)
val small = context.resources.getDimensionPixelSize(R.dimen.spacing_small) val micro = context.resources.getDimensionPixelSize(R.dimen.spacing_micro)
val micro = context.resources.getDimensionPixelSize(R.dimen.spacing_micro) padding(Carousel.Padding(micro, micro, small, small, micro))
padding(Carousel.Padding(micro, micro, small, small, micro))

withModelsFrom(related) { relatedEntry ->
withModelsFrom(related) { relatedEntry -> val relatedShow = relatedEntry.show
val relatedShow = relatedEntry.show DetailsRelatedItemBindingModel_()
DetailsRelatedItemBindingModel_() .id("related_${relatedShow.id}")
.id("related_${relatedShow.id}") .tiviShow(relatedShow)
.tiviShow(relatedShow) .posterImage(relatedEntry.images.findHighestRatedPoster())
.tmdbImageUrlProvider(tmdbImageUrlProvider()) .tmdbImageUrlProvider(tmdbImageUrlProvider())
.clickListener { view -> .clickListener { view -> callbacks?.onRelatedShowClicked(relatedShow, view) }
callbacks?.onRelatedShowClicked(relatedShow, view)
}
}
} }
} }
} }
Expand Down
Expand Up @@ -62,30 +62,34 @@ class ShowDetailsFragmentViewModel @AssistedInject constructor(
) : TiviMvRxViewModel<ShowDetailsViewState>(initialState) { ) : TiviMvRxViewModel<ShowDetailsViewState>(initialState) {
init { init {
observeShowFollowStatus.observe() observeShowFollowStatus.observe()
.distinctUntilChanged()
.execute { .execute {
when (it) { when (it) {
is Success -> copy(isFollowed = it.invoke()) is Success -> copy(isFollowed = it.invoke()!!)
else -> copy(isFollowed = false) else -> copy(isFollowed = false)
} }
} }


observeShowDetails.observe() observeShowDetails.observe()
.distinctUntilChanged()
.execute { .execute {
if (it is Success) { if (it is Success) {
val value = it() val value = it()!!
copy(show = value.show, images = value.images) copy(show = value.show, posterImage = value.poster, backdropImage = value.backdrop)
} else { } else {
this this
} }
} }


observeRelatedShows.observe() observeRelatedShows.observe()
.distinctUntilChanged()
.execute { copy(relatedShows = it) } .execute { copy(relatedShows = it) }


tmdbManager.imageProviderObservable tmdbManager.imageProviderObservable
.execute { copy(tmdbImageUrlProvider = it) } .execute { copy(tmdbImageUrlProvider = it) }


observeShowSeasons.observe() observeShowSeasons.observe()
.distinctUntilChanged()
.execute { copy(seasons = it) } .execute { copy(seasons = it) }


withState { withState {
Expand Down
Expand Up @@ -29,7 +29,8 @@ data class ShowDetailsViewState(
val showId: Long, val showId: Long,
val isFollowed: Boolean = false, val isFollowed: Boolean = false,
val show: TiviShow = TiviShow.EMPTY_SHOW, val show: TiviShow = TiviShow.EMPTY_SHOW,
val images: List<ShowTmdbImage> = emptyList(), val posterImage: ShowTmdbImage? = null,
val backdropImage: ShowTmdbImage? = null,
val relatedShows: Async<List<RelatedShowEntryWithShow>> = Uninitialized, val relatedShows: Async<List<RelatedShowEntryWithShow>> = Uninitialized,
val seasons: Async<List<SeasonWithEpisodesAndWatches>> = Uninitialized, val seasons: Async<List<SeasonWithEpisodesAndWatches>> = Uninitialized,
val expandedSeasonIds: Set<Long> = emptySet(), val expandedSeasonIds: Set<Long> = emptySet(),
Expand Down
67 changes: 33 additions & 34 deletions app/src/main/java/app/tivi/ui/databinding/TiviBindingAdapters.kt
Expand Up @@ -29,7 +29,11 @@ import androidx.core.view.doOnLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import app.tivi.R import app.tivi.R
import app.tivi.data.entities.ImageType
import app.tivi.data.entities.ShowTmdbImage
import app.tivi.data.entities.TmdbImageEntity
import app.tivi.extensions.doOnApplyWindowInsets import app.tivi.extensions.doOnApplyWindowInsets
import app.tivi.extensions.optSaturateOnLoad
import app.tivi.extensions.resolveThemeReferenceResId import app.tivi.extensions.resolveThemeReferenceResId
import app.tivi.tmdb.TmdbImageUrlProvider import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.ui.MaxLinesToggleClickListener import app.tivi.ui.MaxLinesToggleClickListener
Expand All @@ -48,10 +52,10 @@ import kotlin.math.roundToInt
) )
fun loadPoster( fun loadPoster(
view: ImageView, view: ImageView,
path: String?, path: String,
urlProvider: TmdbImageUrlProvider?, urlProvider: TmdbImageUrlProvider,
saturateOnLoad: Boolean? saturateOnLoad: Boolean?
) = loadImage(view, path, urlProvider, saturateOnLoad, "poster", TmdbImageUrlProvider::getPosterUrl) ) = loadImage(view, ShowTmdbImage(path = path, type = ImageType.POSTER, showId = 0), urlProvider, saturateOnLoad)


@BindingAdapter( @BindingAdapter(
"tmdbBackdropPath", "tmdbBackdropPath",
Expand All @@ -61,21 +65,25 @@ fun loadPoster(
) )
fun loadBackdrop( fun loadBackdrop(
view: ImageView, view: ImageView,
path: String?, path: String,
urlProvider: TmdbImageUrlProvider?, urlProvider: TmdbImageUrlProvider,
saturateOnLoad: Boolean? saturateOnLoad: Boolean?
) = loadImage(view, path, urlProvider, saturateOnLoad, "backdrop", TmdbImageUrlProvider::getBackdropUrl) ) = loadImage(view, ShowTmdbImage(path = path, type = ImageType.BACKDROP, showId = 0), urlProvider, saturateOnLoad)


private inline fun loadImage( @BindingAdapter(
"image",
"tmdbImageUrlProvider",
"imageSaturateOnLoad",
requireAll = false
)
fun loadImage(
view: ImageView, view: ImageView,
path: String?, image: TmdbImageEntity?,
urlProvider: TmdbImageUrlProvider?, urlProvider: TmdbImageUrlProvider?,
saturateOnLoad: Boolean?, saturateOnLoad: Boolean?
type: String,
crossinline urlEr: (TmdbImageUrlProvider, String, Int) -> String
) { ) {
if (path != null && urlProvider != null) { if (image != null && urlProvider != null) {
val requestKey = Objects.hash(path, urlProvider, type) val requestKey = Objects.hash(image, urlProvider)
view.setTag(R.id.loading, requestKey) view.setTag(R.id.loading, requestKey)


view.doOnLayout { view.doOnLayout {
Expand All @@ -85,28 +93,19 @@ private inline fun loadImage(
return@doOnLayout return@doOnLayout
} }


fun toUrl(image: TmdbImageEntity, width: Int): String {
return when (image.type) {
ImageType.BACKDROP -> urlProvider.getBackdropUrl(image.path, width)
ImageType.POSTER -> urlProvider.getPosterUrl(image.path, width)
}
}

GlideApp.with(it) GlideApp.with(it)
.let { r -> .optSaturateOnLoad(saturateOnLoad == null || saturateOnLoad)
if (saturateOnLoad == null || saturateOnLoad) { .load(toUrl(image, view.width))
// If we don't have a value, or we're explicitly set the yes, saturate on load .thumbnail(GlideApp.with(view)
r.saturateOnLoad() .optSaturateOnLoad(saturateOnLoad == null || saturateOnLoad)
} else { .load(toUrl(image, 0)))
r.asDrawable()
}
}
.load(urlEr(urlProvider, path, view.width))
.thumbnail(
GlideApp.with(view)
.let { tr ->
if (saturateOnLoad == null || saturateOnLoad) {
// If we don't have a value, or we're explicitly set the yes, saturate on load
tr.saturateOnLoad()
} else {
tr.asDrawable()
}
}
.load(urlEr(urlProvider, path, 0))
)
.into(view) .into(view)
} }
} else { } else {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/app/tivi/util/EntryGridEpoxyController.kt
Expand Up @@ -23,6 +23,7 @@ import androidx.annotation.StringRes
import app.tivi.HeaderBindingModel_ import app.tivi.HeaderBindingModel_
import app.tivi.PosterGridItemBindingModel_ import app.tivi.PosterGridItemBindingModel_
import app.tivi.data.Entry import app.tivi.data.Entry
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.EntryWithShow import app.tivi.data.resultentities.EntryWithShow
import app.tivi.emptyState import app.tivi.emptyState
import app.tivi.header import app.tivi.header
Expand Down Expand Up @@ -85,6 +86,7 @@ open class EntryGridEpoxyController<LI : EntryWithShow<out Entry>>(
return PosterGridItemBindingModel_() return PosterGridItemBindingModel_()
.id(item.generateStableId()) .id(item.generateStableId())
.tmdbImageUrlProvider(tmdbImageUrlProvider) .tmdbImageUrlProvider(tmdbImageUrlProvider)
.posterImage(item.images.findHighestRatedPoster())
.tiviShow(item.show) .tiviShow(item.show)
.transitionName(item.show.homepage) .transitionName(item.show.homepage)
.clickListener(View.OnClickListener { callbacks?.onItemClicked(item) }) .clickListener(View.OnClickListener { callbacks?.onItemClicked(item) })
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/layout/fragment_show_details.xml
Expand Up @@ -44,7 +44,7 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:tmdbBackdropPath="@{state.show.tmdbBackdropPath}" app:image="@{state.backdropImage}"
app:tmdbImageUrlProvider="@{state.tmdbImageUrlProvider.invoke()}" /> app:tmdbImageUrlProvider="@{state.tmdbImageUrlProvider.invoke()}" />


<View <View
Expand All @@ -69,7 +69,7 @@
app:imageSaturateOnLoad="@{false}" app:imageSaturateOnLoad="@{false}"
app:outlineProviderInstance="@{app.tivi.ui.RoundRectViewOutline.INSTANCE}" app:outlineProviderInstance="@{app.tivi.ui.RoundRectViewOutline.INSTANCE}"
app:tmdbImageUrlProvider="@{state.tmdbImageUrlProvider.invoke()}" app:tmdbImageUrlProvider="@{state.tmdbImageUrlProvider.invoke()}"
app:tmdbPosterPath="@{state.show.tmdbPosterPath}" /> app:image="@{state.posterImage}" />


<!-- Needed to fill a rounding error gap in MotionLayout. See https://issuetracker.google.com/112728689 --> <!-- Needed to fill a rounding error gap in MotionLayout. See https://issuetracker.google.com/112728689 -->
<View <View
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/res/layout/view_holder_details_related_item.xml
Expand Up @@ -29,6 +29,10 @@
name="tiviShow" name="tiviShow"
type="app.tivi.data.entities.TiviShow" /> type="app.tivi.data.entities.TiviShow" />


<variable
name="posterImage"
type="app.tivi.data.entities.TmdbImageEntity" />

<variable <variable
name="transitionName" name="transitionName"
type="String" /> type="String" />
Expand Down Expand Up @@ -70,7 +74,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:tmdbImageUrlProvider="@{tmdbImageUrlProvider}" app:tmdbImageUrlProvider="@{tmdbImageUrlProvider}"
app:tmdbPosterPath="@{tiviShow.tmdbPosterPath}" /> app:image="@{posterImage}" />


</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>


Expand Down

0 comments on commit 7c16f44

Please sign in to comment.