Skip to content
Permalink
Browse files

Pipe new entity class for images through

  • Loading branch information...
chrisbanes committed Jun 10, 2019
1 parent 539cb6a commit 7c16f446051f91e8b396c9f49a9e87ad25036974
Showing with 247 additions and 146 deletions.
  1. +26 −0 app/src/main/java/app/tivi/extensions/GlideKtExtensions.kt
  2. +4 −1 app/src/main/java/app/tivi/home/discover/DiscoverEpoxyController.kt
  3. +2 −0 app/src/main/java/app/tivi/home/discover/DiscoverViewModel.kt
  4. +2 −0 app/src/main/java/app/tivi/home/followed/FollowedEpoxyController.kt
  5. +1 −1 app/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt
  6. +2 −0 app/src/main/java/app/tivi/home/watched/WatchedEpoxyController.kt
  7. +26 −28 app/src/main/java/app/tivi/showdetails/details/ShowDetailsEpoxyController.kt
  8. +7 −3 app/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt
  9. +2 −1 app/src/main/java/app/tivi/showdetails/details/ShowDetailsViewState.kt
  10. +33 −34 app/src/main/java/app/tivi/ui/databinding/TiviBindingAdapters.kt
  11. +2 −0 app/src/main/java/app/tivi/util/EntryGridEpoxyController.kt
  12. +2 −2 app/src/main/res/layout/fragment_show_details.xml
  13. +5 −1 app/src/main/res/layout/view_holder_details_related_item.xml
  14. +5 −1 app/src/main/res/layout/view_holder_library_followed_item.xml
  15. +5 −1 app/src/main/res/layout/view_holder_library_watched_item.xml
  16. +5 −1 app/src/main/res/layout/view_holder_poster_grid_item.xml
  17. +5 −1 app/src/main/res/layout/view_holder_search_item_show.xml
  18. +3 −15 data-android/schemas/app.tivi.data.TiviDatabase/21.json
  19. +3 −2 data/src/main/java/app/tivi/data/daos/PairEntryDao.kt
  20. +6 −2 data/src/main/java/app/tivi/data/daos/RelatedShowsDao.kt
  21. +4 −1 data/src/main/java/app/tivi/data/entities/TiviEntity.kt
  22. +0 −2 data/src/main/java/app/tivi/data/entities/TiviShow.kt
  23. +1 −3 data/src/main/java/app/tivi/data/mappers/TmdbBaseShowToTiviShow.kt
  24. +0 −2 data/src/main/java/app/tivi/data/mappers/TmdbShowToTiviShow.kt
  25. +1 −1 data/src/main/java/app/tivi/data/repositories/followedshows/FollowedShowsRepository.kt
  26. +1 −1 data/src/main/java/app/tivi/data/repositories/popularshows/PopularShowsRepository.kt
  27. +2 −4 data/src/main/java/app/tivi/data/repositories/relatedshows/RelatedShowsRepository.kt
  28. +1 −1 data/src/main/java/app/tivi/data/repositories/relatedshows/RelatedShowsStore.kt
  29. +1 −3 data/src/main/java/app/tivi/data/repositories/shows/ShowRepositoryImpl.kt
  30. +1 −1 data/src/main/java/app/tivi/data/repositories/trendingshows/TrendingShowsRepository.kt
  31. +1 −1 data/src/main/java/app/tivi/data/repositories/watchedshows/WatchedShowsRepository.kt
  32. +5 −3 data/src/main/java/app/tivi/data/resultentities/EntryWithShow.kt
  33. +14 −5 data/src/main/java/app/tivi/data/resultentities/FollowedShowEntryWithShow.kt
  34. +12 −4 data/src/main/java/app/tivi/data/resultentities/PopularEntryWithShow.kt
  35. +13 −4 data/src/main/java/app/tivi/data/resultentities/RelatedShowEntryWithShow.kt
  36. +7 −5 data/src/main/java/app/tivi/data/resultentities/ShowDetailed.kt
  37. +13 −4 data/src/main/java/app/tivi/data/resultentities/TrendingEntryWithShow.kt
  38. +13 −4 data/src/main/java/app/tivi/data/resultentities/WatchedShowEntryWithShow.kt
  39. +11 −3 interactors/src/main/java/app/tivi/interactors/UpdateRelatedShows.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()
}
@@ -19,6 +19,7 @@ package app.tivi.home.discover
import app.tivi.HeaderBindingModel_
import app.tivi.R
import app.tivi.data.Entry
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.EntryWithShow
import app.tivi.data.resultentities.PopularEntryWithShow
import app.tivi.data.resultentities.TrendingEntryWithShow
@@ -58,7 +59,8 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider)
tiviShow(item.show)
annotationLabel(item.entry?.watchers.toString())
posterImage(item.images.findHighestRatedPoster())
annotationLabel(item.entry.watchers.toString())
annotationIcon(R.drawable.ic_eye_12dp)
transitionName("trending_${item.show.homepage}")
clickListener { model, _, _, _ ->
@@ -86,6 +88,7 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
posterGridItem {
id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider)
posterImage(item.images.findHighestRatedPoster())
tiviShow(item.show)
transitionName("popular_${item.show.homepage}")
clickListener { model, _, _, _ ->
@@ -49,10 +49,12 @@ class DiscoverViewModel @AssistedInject constructor(
.execute { copy(isLoading = it() ?: false) }

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

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

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

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

withModelsFrom(related) { relatedEntry ->
val relatedShow = relatedEntry.show
DetailsRelatedItemBindingModel_()
.id("related_${relatedShow.id}")
.tiviShow(relatedShow)
.tmdbImageUrlProvider(tmdbImageUrlProvider())
.clickListener { view ->
callbacks?.onRelatedShowClicked(relatedShow, view)
}
}
if (relatedShows is Success) {
val related = relatedShows()
if (related.isNotEmpty()) {
detailsHeader {
id("related_header")
title(R.string.details_related)
spanSizeOverride(TotalSpanOverride)
}
carousel {
id("related_shows")
numViewsToShowOnScreen(5.25f)
hasFixedSize(true)

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

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

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

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

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

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

withState {
@@ -29,7 +29,8 @@ data class ShowDetailsViewState(
val showId: Long,
val isFollowed: Boolean = false,
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 seasons: Async<List<SeasonWithEpisodesAndWatches>> = Uninitialized,
val expandedSeasonIds: Set<Long> = emptySet(),
@@ -29,7 +29,11 @@ import androidx.core.view.doOnLayout
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
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.optSaturateOnLoad
import app.tivi.extensions.resolveThemeReferenceResId
import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.ui.MaxLinesToggleClickListener
@@ -48,10 +52,10 @@ import kotlin.math.roundToInt
)
fun loadPoster(
view: ImageView,
path: String?,
urlProvider: TmdbImageUrlProvider?,
path: String,
urlProvider: TmdbImageUrlProvider,
saturateOnLoad: Boolean?
) = loadImage(view, path, urlProvider, saturateOnLoad, "poster", TmdbImageUrlProvider::getPosterUrl)
) = loadImage(view, ShowTmdbImage(path = path, type = ImageType.POSTER, showId = 0), urlProvider, saturateOnLoad)

@BindingAdapter(
"tmdbBackdropPath",
@@ -61,21 +65,25 @@ fun loadPoster(
)
fun loadBackdrop(
view: ImageView,
path: String?,
urlProvider: TmdbImageUrlProvider?,
path: String,
urlProvider: TmdbImageUrlProvider,
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,
path: String?,
image: TmdbImageEntity?,
urlProvider: TmdbImageUrlProvider?,
saturateOnLoad: Boolean?,
type: String,
crossinline urlEr: (TmdbImageUrlProvider, String, Int) -> String
saturateOnLoad: Boolean?
) {
if (path != null && urlProvider != null) {
val requestKey = Objects.hash(path, urlProvider, type)
if (image != null && urlProvider != null) {
val requestKey = Objects.hash(image, urlProvider)
view.setTag(R.id.loading, requestKey)

view.doOnLayout {
@@ -85,28 +93,19 @@ private inline fun loadImage(
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)
.let { r ->
if (saturateOnLoad == null || saturateOnLoad) {
// If we don't have a value, or we're explicitly set the yes, saturate on load
r.saturateOnLoad()
} else {
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))
)
.optSaturateOnLoad(saturateOnLoad == null || saturateOnLoad)
.load(toUrl(image, view.width))
.thumbnail(GlideApp.with(view)
.optSaturateOnLoad(saturateOnLoad == null || saturateOnLoad)
.load(toUrl(image, 0)))
.into(view)
}
} else {
@@ -23,6 +23,7 @@ import androidx.annotation.StringRes
import app.tivi.HeaderBindingModel_
import app.tivi.PosterGridItemBindingModel_
import app.tivi.data.Entry
import app.tivi.data.entities.findHighestRatedPoster
import app.tivi.data.resultentities.EntryWithShow
import app.tivi.emptyState
import app.tivi.header
@@ -85,6 +86,7 @@ open class EntryGridEpoxyController<LI : EntryWithShow<out Entry>>(
return PosterGridItemBindingModel_()
.id(item.generateStableId())
.tmdbImageUrlProvider(tmdbImageUrlProvider)
.posterImage(item.images.findHighestRatedPoster())
.tiviShow(item.show)
.transitionName(item.show.homepage)
.clickListener(View.OnClickListener { callbacks?.onItemClicked(item) })
@@ -44,7 +44,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tmdbBackdropPath="@{state.show.tmdbBackdropPath}"
app:image="@{state.backdropImage}"
app:tmdbImageUrlProvider="@{state.tmdbImageUrlProvider.invoke()}" />

<View
@@ -69,7 +69,7 @@
app:imageSaturateOnLoad="@{false}"
app:outlineProviderInstance="@{app.tivi.ui.RoundRectViewOutline.INSTANCE}"
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 -->
<View
@@ -29,6 +29,10 @@
name="tiviShow"
type="app.tivi.data.entities.TiviShow" />

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

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

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

0 comments on commit 7c16f44

Please sign in to comment.
You can’t perform that action at this time.