Skip to content
Permalink
Browse files

First steps towards revamp of show details

  • Loading branch information...
chrisbanes committed Nov 7, 2019
1 parent 42a52ca commit 5123301ddb48b2c77c6d4fa552ee0e16a5c670d1
@@ -16,6 +16,9 @@

package app.tivi.common.epoxy

import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.EpoxyController
import com.airbnb.epoxy.EpoxyModel

/** Add models to a CarouselModel_ by transforming a list of items into EpoxyModels.
@@ -28,4 +31,15 @@ inline fun <T> TiviCarouselModelBuilder.withModelsFrom(
modelBuilder: (T) -> EpoxyModel<*>
) {
models(items.map { modelBuilder(it) })
}

fun RecyclerView.syncSpanSizes(controller: EpoxyController) {
val layout = layoutManager
if (layout is GridLayoutManager) {
if (controller.spanCount != layout.spanCount ||
layout.spanSizeLookup !== controller.spanSizeLookup) {
controller.spanCount = layout.spanCount
layout.spanSizeLookup = controller.spanSizeLookup
}
}
}
@@ -20,4 +20,8 @@ import com.airbnb.epoxy.EpoxyModel

object TotalSpanOverride : EpoxyModel.SpanSizeOverrideCallback {
override fun getSpanSize(totalSpanCount: Int, position: Int, itemCount: Int) = totalSpanCount
}

object HalfSpanOverride : EpoxyModel.SpanSizeOverrideCallback {
override fun getSpanSize(totalSpanCount: Int, position: Int, itemCount: Int) = (totalSpanCount / 2).coerceAtLeast(1)
}
@@ -22,6 +22,7 @@ import app.tivi.data.entities.ImageType
import app.tivi.data.entities.ShowTmdbImage
import app.tivi.data.entities.TmdbImageEntity
import coil.api.loadAny
import coil.transform.RoundedCornersTransformation

@BindingAdapter(
"tmdbBackdropPath",
@@ -38,30 +39,40 @@ fun ImageView.loadBackdrop(
loadImage(
null,
oldSaturateOnLoad,
0f,
path?.let { ShowTmdbImage(path = path, type = ImageType.BACKDROP, showId = 0) },
saturateOnLoad
saturateOnLoad,
0f
)
}
}

@BindingAdapter(
"image",
"imageSaturateOnLoad",
"imageCornerRadius",
requireAll = false
)
fun ImageView.loadImage(
oldImage: TmdbImageEntity?,
oldSaturateOnLoad: Boolean?,
oldCornerRadius: Float,
image: TmdbImageEntity?,
saturateOnLoad: Boolean?
saturateOnLoad: Boolean?,
cornerRadius: Float
) {
if (oldImage == image && oldSaturateOnLoad == saturateOnLoad) return
if (oldImage == image &&
oldSaturateOnLoad == saturateOnLoad &&
oldCornerRadius == cornerRadius) return

loadAny(image) {
if (saturateOnLoad == null || saturateOnLoad == true) {
val saturatingTarget = SaturatingImageViewTarget(this@loadImage)
target(saturatingTarget)
listener(saturatingTarget)
}
if (cornerRadius > 0) {
transformations(RoundedCornersTransformation(cornerRadius))
}
}
}
@@ -16,10 +16,10 @@
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:animateLayoutChanges="true">
android:paddingEnd="16dp"
android:animateLayoutChanges="true"
android:orientation="vertical">

</LinearLayout>
@@ -36,7 +36,6 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:contentDescription="@{contentDescription}"
android:transitionGroup="true">

@@ -21,6 +21,7 @@ import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.forEach
import app.tivi.common.epoxy.HalfSpanOverride
import app.tivi.common.epoxy.TotalSpanOverride
import app.tivi.common.epoxy.tiviCarousel
import app.tivi.common.epoxy.withModelsFrom
@@ -67,7 +68,7 @@ class ShowDetailsEpoxyController @Inject constructor(
}

override fun buildModels() {
buildShowModels(state.show)
buildShowModels(state)

val episodeWithSeason = state.nextEpisodeToWatch()
if (episodeWithSeason?.episode != null) {
@@ -93,7 +94,14 @@ class ShowDetailsEpoxyController @Inject constructor(
buildSeasonsModels(state.viewStats, state.seasons, state.expandedSeasonIds)
}

private fun buildShowModels(show: TiviShow) {
private fun buildShowModels(state: ShowDetailsViewState) {
detailsPosterItem {
id("poster")
posterImage(state.posterImage)
spanSizeOverride(HalfSpanOverride)
}

val show = state.show
val badges = ArrayList<DetailsBadgeBindingModel_>()
show.traktRating?.let { rating ->
badges += DetailsBadgeBindingModel_().apply {
@@ -132,7 +140,8 @@ class ShowDetailsEpoxyController @Inject constructor(
}
}
if (badges.isNotEmpty()) {
EpoxyModelGroup(R.layout.layout_badge_holder, badges).addTo(this)
EpoxyModelGroup(R.layout.layout_badge_holder, badges)
.addTo(this)
}

detailsHeader {
@@ -168,6 +177,7 @@ class ShowDetailsEpoxyController @Inject constructor(
id("related_shows")
itemWidth(context.resources.getDimensionPixelSize(R.dimen.related_shows_item_width))
hasFixedSize(true)
spanSizeOverride(TotalSpanOverride)

val vert = context.resources.getDimensionPixelSize(R.dimen.spacing_small)
val horiz = context.resources.getDimensionPixelSize(R.dimen.spacing_normal)
@@ -31,6 +31,7 @@ import androidx.fragment.app.commitNow
import androidx.recyclerview.widget.LinearSmoothScroller
import app.tivi.SharedElementHelper
import app.tivi.TiviFragmentWithBinding
import app.tivi.common.epoxy.syncSpanSizes
import app.tivi.data.entities.ActionDate
import app.tivi.data.entities.Episode
import app.tivi.data.entities.Season
@@ -175,6 +176,9 @@ class ShowDetailsFragment : TiviFragmentWithBinding<FragmentShowDetailsBinding>(

binding.detailsRv.apply {
adapter = controller.adapter
syncSpanSizes(controller)
setHasFixedSize(true)

tintPainter = TintPainter.completeList(
context.resolveThemeColor(R.attr.colorSurface),
opacity = 0.7f
@@ -33,8 +33,7 @@
android:id="@+id/details_motion"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutDescription="@xml/scene_show_details"
app:layout_optimizationLevel="standard">
app:layoutDescription="@xml/scene_show_details">

<ImageView
android:id="@+id/details_backdrop"
@@ -59,20 +58,6 @@
app:layout_constraintStart_toStartOf="@id/details_backdrop"
app:layout_constraintTop_toTopOf="@id/details_backdrop" />

<ImageView
android:id="@+id/details_poster"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/placeholder_48dp"
android:scaleType="centerCrop"
android:transformPivotX="0px"
android:transformPivotY="0px"
android:transitionName="poster"
app:clipToOutline="@{true}"
app:imageSaturateOnLoad="@{false}"
app:outlineProviderInstance="@{app.tivi.ui.RoundRectViewOutline.INSTANCE}"
app:image="@{state.posterImage}" />

<!-- Needed to fill a rounding error gap in MotionLayout. See https://issuetracker.google.com/112728689 -->
<View
android:id="@+id/details_gap_filler"
@@ -81,12 +66,11 @@
android:background="?android:attr/colorBackground"
app:layout_constraintBottom_toTopOf="@id/details_rv" />

<app.tivi.ui.widget.TopLeftCutoutBackgroundView
<View
android:id="@+id/details_appbar_background"
android:layout_width="0dp"
android:layout_height="0dp"
app:backgroundColor="?android:attr/colorBackground"
app:topLeftCutSize="@dimen/details_corner_cutout" />
android:background="?android:attr/colorBackground" />

<TextView
android:id="@+id/details_title"
@@ -126,7 +110,8 @@
android:background="?android:attr/colorBackground"
android:clipToPadding="false"
android:paddingBottom="@dimen/spacing_normal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

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

<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/spacing_normal">

<app.tivi.ui.widget.TwoThreeImageView
android:id="@+id/show_poster"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:imageSaturateOnLoad="@{false}"
app:image="@{posterImage}"
android:transitionName="poster"
app:imageCornerRadius="@{@dimen/image_round_rect_radius}"/>

</FrameLayout>
</layout>
@@ -36,15 +36,9 @@
android:id="@id/details_appbar_background"
android:layout_width="0dp"
android:layout_height="@dimen/details_corner_cutout"
app:layout_constraintBottom_toBottomOf="@id/details_backdrop"
app:layout_constraintTop_toBottomOf="@id/details_backdrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">

<CustomAttribute
app:attributeName="cutProgress"
app:customFloatValue="1.0" />

</Constraint>
app:layout_constraintStart_toStartOf="parent" />

<Constraint
android:id="@id/details_title"
@@ -56,19 +50,9 @@
android:layout_marginTop="@dimen/spacing_micro"
app:layout_constraintBottom_toBottomOf="@id/details_appbar_background"
app:layout_constraintEnd_toStartOf="@id/details_follow_fab"
app:layout_constraintStart_toEndOf="@id/details_poster"
app:layout_constraintStart_toStartOf="@id/details_appbar_background"
app:layout_constraintTop_toTopOf="@id/details_appbar_background" />

<Constraint
android:id="@id/details_poster"
android:layout_width="80dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/spacing_normal"
android:elevation="@dimen/details_poster_elevation"
app:layout_constraintBottom_toBottomOf="@id/details_appbar_background"
app:layout_constraintDimensionRatio="h,1:1.5"
app:layout_constraintStart_toStartOf="@id/details_rv" />

<Constraint android:id="@id/details_status_bar_anchor">

<CustomAttribute
@@ -101,13 +85,7 @@
android:elevation="@dimen/elevation_appbar"
app:layout_constraintEnd_toEndOf="@id/details_rv"
app:layout_constraintStart_toStartOf="@id/details_rv"
app:layout_constraintTop_toBottomOf="@id/details_status_bar_anchor">

<CustomAttribute
app:attributeName="cutProgress"
app:customFloatValue="0.0" />

</Constraint>
app:layout_constraintTop_toBottomOf="@id/details_status_bar_anchor" />

<Constraint
android:id="@id/details_title"
@@ -123,15 +101,6 @@
app:layout_constraintStart_toStartOf="@id/details_appbar_background"
app:layout_constraintTop_toTopOf="@id/details_appbar_background" />

<Constraint
android:id="@id/details_poster"
android:layout_width="72dp"
android:layout_height="0dp"
android:elevation="@dimen/details_poster_not_elevation"
app:layout_constraintDimensionRatio="h,1:1.5"
app:layout_constraintStart_toStartOf="@id/details_title"
app:layout_constraintTop_toBottomOf="@id/details_status_bar_anchor" />

<!-- This gives us our parallax effect -->
<Constraint android:id="@id/details_backdrop">
<Transform android:translationY="-80dp" />
@@ -164,20 +133,6 @@
app:motionInterpolator="easeInOut">

<KeyFrameSet>
<KeyAttribute
android:elevation="@dimen/details_poster_elevation"
android:rotation="47"
android:rotationY="15"
app:framePosition="20"
app:motionTarget="@id/details_poster" />

<!-- This looks weird. We need a quick change from elevated to not-so-elevated at 18%
so we set 2 key attributes, one at 21% and other at 25%. -->
<KeyAttribute
android:elevation="@dimen/details_poster_not_elevation"
app:framePosition="25"
app:motionTarget="@id/details_poster" />

<KeyAttribute
android:elevation="0dp"
app:framePosition="75"
@@ -207,12 +162,6 @@
app:framePosition="70"
app:motionTarget="@id/details_follow_fab" />

<KeyPosition
app:framePosition="20"
app:keyPositionType="deltaRelative"
app:motionTarget="@id/details_poster"
app:percentY="0.5" />

</KeyFrameSet>

<OnSwipe

0 comments on commit 5123301

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