Skip to content

Commit

Permalink
Fix image posters randomly loading
Browse files Browse the repository at this point in the history
Multiple things going on here:

- TmdbImageUrlProvider was not a data class, so
  didn't implement equals()
- BindingAdapter uses doOnLayout, which is not cleared
  if the item changes before the layout pass.

Closes #275
  • Loading branch information
chrisbanes committed Apr 18, 2019
1 parent 101c8e7 commit 432e882
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 43 deletions.
102 changes: 70 additions & 32 deletions app/src/main/java/app/tivi/ui/databinding/TiviBindingAdapters.kt
Expand Up @@ -27,6 +27,7 @@ import android.widget.TextView
import androidx.core.view.doOnLayout 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.extensions.doOnApplyWindowInsets import app.tivi.extensions.doOnApplyWindowInsets
import app.tivi.extensions.resolveThemeReferenceResId import app.tivi.extensions.resolveThemeReferenceResId
import app.tivi.tmdb.TmdbImageUrlProvider import app.tivi.tmdb.TmdbImageUrlProvider
Expand All @@ -35,45 +36,83 @@ import app.tivi.ui.glide.GlideApp
import app.tivi.util.ScrimUtil import app.tivi.util.ScrimUtil
import com.google.android.material.shape.CornerFamily import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import java.util.Objects
import kotlin.math.roundToInt import kotlin.math.roundToInt


@BindingAdapter("tmdbPosterPath", "tmdbImageUrlProvider", "imageSaturateOnLoad") @BindingAdapter(
fun loadPoster(view: ImageView, path: String?, urlProvider: TmdbImageUrlProvider?, saturateOnLoad: Boolean?) { "tmdbPosterPath",
if (path != null && urlProvider != null) { "tmdbImageUrlProvider",
view.doOnLayout { "imageSaturateOnLoad",
GlideApp.with(view) requireAll = false
.let { r -> if (saturateOnLoad == true) r.saturateOnLoad() else r.asDrawable() } )
.load(urlProvider.getPosterUrl(path, it.width)) fun loadPoster(
.thumbnail(GlideApp.with(view).load(urlProvider.getPosterUrl(path, 0))) view: ImageView,
.into(view) path: String?,
} urlProvider: TmdbImageUrlProvider?,
} else { saturateOnLoad: Boolean?
GlideApp.with(view).clear(view) ) {
} loadImage(view, path, urlProvider, saturateOnLoad, "poster", TmdbImageUrlProvider::getPosterUrl)
}

@BindingAdapter("tmdbPosterPath", "tmdbImageUrlProvider")
fun loadPoster(view: ImageView, path: String?, urlProvider: TmdbImageUrlProvider?) {
loadPoster(view, path, urlProvider, true)
}

@BindingAdapter("tmdbBackdropPath", "tmdbImageUrlProvider")
fun loadBackdrop(view: ImageView, path: String?, urlProvider: TmdbImageUrlProvider?) {
loadBackdrop(view, path, urlProvider, true)
} }


@BindingAdapter("tmdbBackdropPath", "tmdbImageUrlProvider", "imageSaturateOnLoad") @BindingAdapter(
fun loadBackdrop(view: ImageView, path: String?, urlProvider: TmdbImageUrlProvider?, saturateOnLoad: Boolean?) { "tmdbBackdropPath",
"tmdbImageUrlProvider",
"imageSaturateOnLoad",
requireAll = false
)
fun loadBackdrop(
view: ImageView,
path: String?,
urlProvider: TmdbImageUrlProvider?,
saturateOnLoad: Boolean?
) = loadImage(view, path, urlProvider, saturateOnLoad, "backdrop", TmdbImageUrlProvider::getBackdropUrl)

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

view.doOnLayout { view.doOnLayout {
GlideApp.with(view) if (it.getTag(R.id.loading) != requestKey) {
.let { r -> if (saturateOnLoad == true) r.saturateOnLoad() else r.asDrawable() } // The request key is different, exit now since there's we've probably be rebound to a different
.load(urlProvider.getBackdropUrl(path, it.width)) // item
.thumbnail(GlideApp.with(view).load(urlProvider.getBackdropUrl(path, 0))) return@doOnLayout
}

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))
)
.into(view) .into(view)
} }
} else { } else {
GlideApp.with(view).clear(view) GlideApp.with(view).clear(view)
view.setImageDrawable(null)
} }
} }


Expand Down Expand Up @@ -154,13 +193,12 @@ fun applySystemWindows(
systemWindowRight: Boolean, systemWindowRight: Boolean,
systemWindowBottom: Boolean systemWindowBottom: Boolean
) { ) {
view.doOnApplyWindowInsets { view, insets, paddingState -> view.doOnApplyWindowInsets { v, insets, paddingState ->
val left = if (systemWindowLeft) insets.systemWindowInsetLeft else 0 val left = if (systemWindowLeft) insets.systemWindowInsetLeft else 0
val top = if (systemWindowTop) insets.systemWindowInsetTop else 0 val top = if (systemWindowTop) insets.systemWindowInsetTop else 0
val right = if (systemWindowRight) insets.systemWindowInsetRight else 0 val right = if (systemWindowRight) insets.systemWindowInsetRight else 0
val bottom = if (systemWindowBottom) insets.systemWindowInsetBottom else 0 val bottom = if (systemWindowBottom) insets.systemWindowInsetBottom else 0

v.setPadding(
view.setPadding(
paddingState.left + left, paddingState.left + left,
paddingState.top + top, paddingState.top + top,
paddingState.right + right, paddingState.right + right,
Expand Down
Expand Up @@ -31,6 +31,6 @@ interface EntryWithShow<ET : Entry> {
} }


fun generateStableId(): Long { fun generateStableId(): Long {
return Objects.hash(entry!!::class, show.id).toLong() return Objects.hash(entry!!::class.java.name, show.id).toLong()
} }
} }
6 changes: 3 additions & 3 deletions tmdb/src/main/java/app/tivi/tmdb/TmdbImageSizes.kt
Expand Up @@ -18,9 +18,9 @@ package app.tivi.tmdb


object TmdbImageSizes { object TmdbImageSizes {


val baseImageUrl = "https://image.tmdb.org/t/p/" const val baseImageUrl = "https://image.tmdb.org/t/p/"


val posterSizes = arrayOf( val posterSizes = listOf(
"w92", "w92",
"w154", "w154",
"w185", "w185",
Expand All @@ -30,7 +30,7 @@ object TmdbImageSizes {
"original" "original"
) )


val backdropSizes = arrayOf( val backdropSizes = listOf(
"w300", "w300",
"w780", "w780",
"w1280", "w1280",
Expand Down
8 changes: 4 additions & 4 deletions tmdb/src/main/java/app/tivi/tmdb/TmdbImageUrlProvider.kt
Expand Up @@ -18,10 +18,10 @@ package app.tivi.tmdb


private val IMAGE_SIZE_PATTERN = "w(\\d+)$".toRegex() private val IMAGE_SIZE_PATTERN = "w(\\d+)$".toRegex()


class TmdbImageUrlProvider( data class TmdbImageUrlProvider(
private val baseImageUrl: String = TmdbImageSizes.baseImageUrl, private val baseImageUrl: String = TmdbImageSizes.baseImageUrl,
private val posterSizes: Array<String> = TmdbImageSizes.posterSizes, private val posterSizes: List<String> = TmdbImageSizes.posterSizes,
private val backdropSizes: Array<String> = TmdbImageSizes.backdropSizes private val backdropSizes: List<String> = TmdbImageSizes.backdropSizes
) { ) {
fun getPosterUrl(path: String, imageWidth: Int): String { fun getPosterUrl(path: String, imageWidth: Int): String {
return "$baseImageUrl${selectSize(posterSizes, imageWidth)}$path" return "$baseImageUrl${selectSize(posterSizes, imageWidth)}$path"
Expand All @@ -31,7 +31,7 @@ class TmdbImageUrlProvider(
return "$baseImageUrl${selectSize(backdropSizes, imageWidth)}$path" return "$baseImageUrl${selectSize(backdropSizes, imageWidth)}$path"
} }


private fun selectSize(sizes: Array<String>, imageWidth: Int): String { private fun selectSize(sizes: List<String>, imageWidth: Int): String {
var previousSize: String? = null var previousSize: String? = null
var previousWidth = 0 var previousWidth = 0


Expand Down
6 changes: 3 additions & 3 deletions tmdb/src/main/java/app/tivi/tmdb/TmdbManager.kt
Expand Up @@ -59,9 +59,9 @@ class TmdbManager @Inject constructor(
private fun onConfigurationLoaded(configuration: Configuration) { private fun onConfigurationLoaded(configuration: Configuration) {
configuration.images?.let { configuration.images?.let {
val newProvider = TmdbImageUrlProvider( val newProvider = TmdbImageUrlProvider(
it.secure_base_url, it.secure_base_url!!,
it.poster_sizes.toTypedArray(), it.poster_sizes ?: emptyList(),
it.backdrop_sizes.toTypedArray()) it.backdrop_sizes ?: emptyList())
imageProviderSubject.onNext(newProvider) imageProviderSubject.onNext(newProvider)
} }
} }
Expand Down

0 comments on commit 432e882

Please sign in to comment.