Skip to content
Permalink
Browse files

Merge pull request #294 from chrisbanes/cb/headers

Sticky headers
  • Loading branch information...
chrisbanes committed Apr 15, 2019
2 parents a66be5e + ff1b976 commit 696d72cd5f1787626f173c84f718299d4d5c32b7
@@ -16,30 +16,31 @@

package app.tivi.extensions

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.core.content.res.getResourceIdOrThrow
import androidx.core.content.res.use

private val typedValue = TypedValue()

@SuppressLint("Recycle")
fun Context.resolveThemeColor(@AttrRes resId: Int, defaultColor: Int = Color.MAGENTA): Int {
return if (theme.resolveAttribute(resId, typedValue, true)) {
if (typedValue.type == TypedValue.TYPE_STRING) {
getColor(typedValue.resourceId)
} else {
typedValue.data
}
} else {
defaultColor
return obtainStyledAttributes(intArrayOf(resId)).use {
it.getColor(0, defaultColor)
}
}

@SuppressLint("Recycle")
fun Context.resolveThemeColorStateList(@AttrRes resId: Int): ColorStateList? {
return obtainStyledAttributes(intArrayOf(resId)).use {
it.getColorStateList(0)
}
}

fun Context.resolveThemeReference(@AttrRes resId: Int): Int {
if (theme.resolveAttribute(resId, typedValue, true)) {
if (typedValue.type == TypedValue.TYPE_REFERENCE) {
return typedValue.resourceId
}
@SuppressLint("Recycle")
fun Context.resolveThemeReferenceResId(@AttrRes resId: Int): Int {
return obtainStyledAttributes(intArrayOf(resId)).use {
it.getResourceIdOrThrow(0)
}
throw IllegalArgumentException("Attribute can not be resolved")
}
@@ -16,6 +16,7 @@

package app.tivi.home.discover

import app.tivi.HeaderBindingModel_
import app.tivi.R
import app.tivi.data.Entry
import app.tivi.data.resultentities.EntryWithShow
@@ -25,6 +26,7 @@ import app.tivi.emptyState
import app.tivi.header
import app.tivi.posterGridItem
import app.tivi.ui.epoxy.TotalSpanOverride
import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.TypedEpoxyController
import javax.inject.Inject

@@ -51,7 +53,7 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
}
}
if (trendingShows.isNotEmpty()) {
trendingShows.take(spanCount * 2).forEach { item ->
trendingShows.take(trendingShows.size - (trendingShows.size % spanCount)).forEach { item ->
posterGridItem {
id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider)
@@ -80,7 +82,7 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
}
}
if (popularShows.isNotEmpty()) {
popularShows.take(spanCount * 2).forEach { item ->
popularShows.take(popularShows.size - (popularShows.size % spanCount)).forEach { item ->
posterGridItem {
id(item.generateStableId())
tmdbImageUrlProvider(tmdbImageUrlProvider)
@@ -98,4 +100,8 @@ class DiscoverEpoxyController @Inject constructor() : TypedEpoxyController<Disco
}
}
}

fun isHeader(model: EpoxyModel<*>): Boolean {
return model is HeaderBindingModel_
}
}
@@ -31,6 +31,7 @@ import app.tivi.extensions.toActivityNavigatorExtras
import app.tivi.extensions.toFragmentNavigatorExtras
import app.tivi.ui.ListItemSharedElementHelper
import app.tivi.ui.SpacingItemDecorator
import app.tivi.ui.epoxy.StickyHeaderScrollListener
import app.tivi.util.GridToGridTransitioner
import app.tivi.util.TiviMvRxFragment
import com.airbnb.mvrx.fragmentViewModel
@@ -67,6 +68,7 @@ internal class DiscoverFragment : TiviMvRxFragment() {
binding.summaryRv.apply {
setController(controller)
addItemDecoration(SpacingItemDecorator(paddingLeft))
addOnScrollListener(StickyHeaderScrollListener(controller, controller::isHeader, binding.headerHolder))
}

controller.callbacks = object : DiscoverEpoxyController.Callbacks {
@@ -19,9 +19,12 @@ package app.tivi.home.followed
import android.os.Handler
import android.os.Looper
import android.view.View
import app.tivi.HeaderBindingModel_
import app.tivi.LibraryFollowedItemBindingModel_
import app.tivi.R
import app.tivi.data.resultentities.FollowedShowEntryWithShow
import app.tivi.emptyState
import app.tivi.header
import app.tivi.home.HomeTextCreator
import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.ui.epoxy.EpoxyModelProperty
@@ -48,6 +51,10 @@ class FollowedEpoxyController @Inject constructor(
spanSizeOverride(TotalSpanOverride)
}
} else {
header {
id("header")
title(R.string.library_followed_shows)
}
super.addModels(models)
}
}
@@ -70,6 +77,10 @@ class FollowedEpoxyController @Inject constructor(
}
}

fun isHeader(model: EpoxyModel<*>): Boolean {
return model is HeaderBindingModel_
}

interface Callbacks {
fun onItemClicked(item: FollowedShowEntryWithShow)
}
@@ -27,6 +27,7 @@ import app.tivi.databinding.FragmentLibraryFollowedBinding
import app.tivi.extensions.toActivityNavigatorExtras
import app.tivi.ui.ListItemSharedElementHelper
import app.tivi.ui.SpacingItemDecorator
import app.tivi.ui.epoxy.StickyHeaderScrollListener
import app.tivi.util.TiviMvRxFragment
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@@ -56,6 +57,7 @@ class FollowedFragment : TiviMvRxFragment() {

binding.followedRv.apply {
addItemDecoration(SpacingItemDecorator(paddingLeft))
addOnScrollListener(StickyHeaderScrollListener(controller, controller::isHeader, binding.headerHolder))
}

controller.callbacks = object : FollowedEpoxyController.Callbacks {
@@ -19,9 +19,12 @@ package app.tivi.home.watched
import android.os.Handler
import android.os.Looper
import android.view.View
import app.tivi.HeaderBindingModel_
import app.tivi.LibraryWatchedItemBindingModel_
import app.tivi.R
import app.tivi.data.resultentities.WatchedShowEntryWithShow
import app.tivi.emptyState
import app.tivi.header
import app.tivi.home.HomeTextCreator
import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.ui.epoxy.EpoxyModelProperty
@@ -51,6 +54,10 @@ class WatchedEpoxyController @Inject constructor(
spanSizeOverride(TotalSpanOverride)
}
} else {
header {
id("header")
title(R.string.library_watched)
}
super.addModels(models)
}
}
@@ -74,6 +81,10 @@ class WatchedEpoxyController @Inject constructor(
}
}

fun isHeader(model: EpoxyModel<*>): Boolean {
return model is HeaderBindingModel_
}

interface Callbacks {
fun onItemClicked(item: WatchedShowEntryWithShow)
}
@@ -27,6 +27,7 @@ import app.tivi.databinding.FragmentLibraryWatchedBinding
import app.tivi.extensions.toActivityNavigatorExtras
import app.tivi.ui.ListItemSharedElementHelper
import app.tivi.ui.SpacingItemDecorator
import app.tivi.ui.epoxy.StickyHeaderScrollListener
import app.tivi.util.TiviMvRxFragment
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
@@ -67,6 +68,7 @@ class WatchedFragment : TiviMvRxFragment() {

binding.watchedRv.apply {
addItemDecoration(SpacingItemDecorator(paddingLeft))
addOnScrollListener(StickyHeaderScrollListener(controller, controller::isHeader, binding.headerHolder))
setController(controller)
}

@@ -18,21 +18,24 @@ package app.tivi.ui.databinding

import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.Outline
import android.view.Gravity
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.ImageView
import android.widget.TextView
import androidx.core.view.doOnLayout
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
import app.tivi.extensions.doOnApplyWindowInsets
import app.tivi.extensions.resolveThemeReference
import app.tivi.extensions.resolveThemeReferenceResId
import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.ui.MaxLinesToggleClickListener
import app.tivi.ui.glide.GlideApp
import app.tivi.util.ScrimUtil
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import kotlin.math.roundToInt

@BindingAdapter("tmdbPosterPath", "tmdbImageUrlProvider", "imageSaturateOnLoad")
fun loadPoster(view: ImageView, path: String?, urlProvider: TmdbImageUrlProvider?, saturateOnLoad: Boolean?) {
@@ -122,9 +125,19 @@ fun materialBackdropBackground(view: View, radius: Float) {
}
}

@BindingAdapter("topCornerOutlineProvider")
fun topCornerOutlineProvider(view: View, radius: Float) {
view.clipToOutline = true
view.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(0, 0, view.width, view.height + radius.roundToInt(), radius)
}
}
}

@BindingAdapter("textAppearanceAttr")
fun textAppearanceAttr(view: TextView, textAppearanceStyleAttr: Int) {
view.setTextAppearance(view.context.resolveThemeReference(textAppearanceStyleAttr))
view.setTextAppearance(view.context.resolveThemeReferenceResId(textAppearanceStyleAttr))
}

@BindingAdapter(
Oops, something went wrong.

0 comments on commit 696d72c

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