Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Welcome: Standardize usage of 'Cell' ViewModels #291

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
package edu.artic.events.recyclerview
package edu.artic.decoration

import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.View
import edu.artic.content.listing.R
import edu.artic.events.AllEventsAdapter
import edu.artic.events.AllEventsCellHeaderViewModel
import edu.artic.events.AllEventsCellViewModel
import edu.artic.events.EventCellViewModel

/**
* The original version of this file was written by
* [edwardaa](https://stackoverflow.com/users/3414249/edwardaa) for
* [https://stackoverflow.com/a/30701422](https://stackoverflow.com/a/30701422).
*/
class AllEventsItemDecoration(
context: Context,
private val spanCount: Int,
override val spanCount: Int,
private val adapter: AllEventsAdapter
) : RecyclerView.ItemDecoration() {
private val horizontalSpacing: Int = context.resources.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_horizontal)
private val verticalSpacing: Int = context.resources.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_vertical)
private val headerVerticalSpacing: Int = context.resources.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_vertical_header)
) : GridItemDecoration(spanCount) {

override fun createDimensions(res: Resources): Dimensions {
return object : Dimensions {
override val horizontal: Int = res.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_horizontal)
override val vertical: Int = res.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_vertical)
override val topMostVertical: Int = res.getDimensionPixelOffset(R.dimen.all_tour_cell_spacing_vertical_header)
}
}

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)

val horizontalSpacing = dimensions.horizontal
val verticalSpacing = dimensions.vertical
val headerVerticalSpacing = dimensions.topMostVertical

// item position
val position = parent.getChildAdapterPosition(view)
adapter.getItemOrNull(position)?.let {

val halfOfVertical = (verticalSpacing / 2.0f).toInt()
val halfOfVertical = dimensions.halfOfVertical()

when (it) {
is AllEventsCellHeaderViewModel -> {
Expand All @@ -32,7 +47,7 @@ class AllEventsItemDecoration(
outRect.top = if (position == 0) headerVerticalSpacing else verticalSpacing
outRect.bottom = halfOfVertical
}
is AllEventsCellViewModel -> {
is EventCellViewModel -> {
val adjustedPosition = position - (it.headerPosition - 1)
val column = (adjustedPosition) % spanCount // item column
outRect.left = horizontalSpacing - column * horizontalSpacing / spanCount // spacing - column * ((1f / spanCount) * spacing)
Expand All @@ -44,4 +59,4 @@ class AllEventsItemDecoration(
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package edu.artic.decoration

import android.content.res.Resources
import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.View
import edu.artic.content.listing.R

/**
* The original version of this file was written by
* [edwardaa](https://stackoverflow.com/users/3414249/edwardaa) for
* [https://stackoverflow.com/a/30701422](https://stackoverflow.com/a/30701422).
*/
class AllExhibitionsItemDecoration(
override val spanCount: Int
) : GridItemDecoration(spanCount) {

override fun createDimensions(res: Resources): Dimensions {
return object : Dimensions {
override val horizontal: Int = res.getDimensionPixelSize(R.dimen.all_exhibitions_cell_spacing_horizontal)
override val vertical: Int = res.getDimensionPixelSize(R.dimen.all_exhibitions_cell_spacing_vertical)
override val topMostVertical: Int = vertical
}
}

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)

val horizontalSpacing = dimensions.horizontal
val verticalSpacing = dimensions.vertical

val adjustedPos = parent.getChildAdapterPosition(view) - 1 // item position, minus 1 for the header
val column = (adjustedPos) % spanCount // item column

outRect.left = horizontalSpacing - column * horizontalSpacing / spanCount // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * horizontalSpacing / spanCount // (column + 1) * ((1f / spanCount) * spacing)

if (adjustedPos < spanCount) { // top edge
outRect.top = verticalSpacing
}
outRect.bottom = verticalSpacing // item bottom

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package edu.artic.decoration

import android.content.res.Resources
import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.view.View
import edu.artic.content.listing.R

/**
* The original version of this file was written by
* [edwardaa](https://stackoverflow.com/users/3414249/edwardaa) for
* [https://stackoverflow.com/a/30701422](https://stackoverflow.com/a/30701422).
*/
class AllToursItemDecoration(
override val spanCount: Int
) : GridItemDecoration(spanCount) {

override fun createDimensions(res: Resources): Dimensions {
return object : Dimensions {
override val horizontal: Int = res.getDimensionPixelSize(R.dimen.all_tour_cell_spacing_horizontal)
override val vertical: Int = res.getDimensionPixelSize(R.dimen.all_tour_cell_spacing_vertical)
override val topMostVertical: Int = vertical
}
}

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)

val horizontalSpacing = dimensions.horizontal
val verticalSpacing = dimensions.vertical

val adjustedPos = parent.getChildAdapterPosition(view) - 1 // item position, minus 1 for the header
if (adjustedPos < 0) {
outRect.setEmpty()
} else {
val column = (adjustedPos) % spanCount // item column

outRect.left = horizontalSpacing - column * horizontalSpacing / spanCount // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * horizontalSpacing / spanCount // (column + 1) * ((1f / spanCount) * spacing)

if (adjustedPos < spanCount) { // top edge
outRect.top = verticalSpacing
}
outRect.bottom = verticalSpacing // item bottom
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package edu.artic.decoration

import android.content.res.Resources
import android.graphics.Rect
import android.support.annotation.CallSuper
import android.support.v7.widget.RecyclerView
import android.view.View

/**
* Simple ItemDecoration that keeps track of useful [Dimensions].
*
* This makes it straightforward to load the best values in the current
* [Resources] - just
* 1. implement [createDimensions]
* 2. call `super.getItemOffsets(outRect, view, parent, state)` at the start of
* your override of [getItemOffsets]
* 3. use [dimensions] in the rest of [getItemOffsets] as needed
*
* @author Philip Cohn-Cort (Fuzz)
*/
abstract class GridItemDecoration(protected open val spanCount: Int = 1) : RecyclerView.ItemDecoration() {

/**
* Use this to simplify implementations of [getItemOffsets] and [onDrawOver] (where applicable).
*
* Implementors are free to interpret these values as they see fit, but we've
* attached some recommended usage in the below docs.
*/
protected interface Dimensions {
/**
* Preferred vertical offset, in pixels.
*/
val vertical : Int
/**
* Preferred horizontal offset, in pixels.
*/
val horizontal : Int
/**
* Preferred vertical offset for the first item or row of items in the [RecyclerView].
*/
val topMostVertical : Int

/**
* Convenience function for subclasses which wish to use [vertical] for
* both top and bottom margins.
*/
fun halfOfVertical() : Int = (vertical / 2.0f).toInt()
}

/**
* Access the singleton created in [createDimensions].
*/
protected lateinit var dimensions: Dimensions

protected abstract fun createDimensions(res: Resources) : Dimensions

/**
* Represents information about whether [dimensions] has a value yet.
*/
private fun needsDimensions(): Boolean {
return !this::dimensions.isInitialized
}

/**
* Initialize [dimensions] by calling [createDimensions], if appropriate.
*
* See [here][RecyclerView.ItemDecoration.getItemOffsets] for more details about
* what to add to overrides of this function.
*/
@CallSuper
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
if (needsDimensions()) {
dimensions = createDimensions(view.resources)
}
}
}
19 changes: 19 additions & 0 deletions content_listing/src/main/kotlin/edu/artic/decoration/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
These 'ItemDecoration' files were originally copied, without attribution,
from https://stackoverflow.com/a/30701422. By adding this file, we
restore that license: the CC BY-SA 3.0. The original author of this
`GridSpacingItemDecoration` code is
[edwardaa](https://stackoverflow.com/users/3414249/edwardaa).

This directory is licensed under the Creative Commons
Attribution-ShareAlike 3.0 United States License. To view a copy of this
license, visit http://creativecommons.org/licenses/by-sa/3.0/us/ or send
a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.



All subsequent modifications to this directory within the Art Institute
of Chicago codebase are released under a dual license, specified below.

You may choose to use this code under the terms of either the AGPL v3
(provided in the top-level `LICENSE` file) or under the terms of the
aforementioned Creative Commons License.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class AllEventsAdapter : AutoHolderRecyclerViewAdapter<AllEventsCellBaseViewMode

override fun View.onBindView(item: AllEventsCellBaseViewModel, position: Int) {

if (item is AllEventsCellViewModel) {
if (item is EventCellViewModel) {

image.visibility = View.VISIBLE
title.visibility = View.VISIBLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ import edu.artic.adapter.itemClicksWithPosition
import edu.artic.analytics.ScreenName
import edu.artic.base.utils.asDeepLinkIntent
import edu.artic.content.listing.R
import edu.artic.events.recyclerview.AllEventsItemDecoration
import edu.artic.decoration.AllEventsItemDecoration
import edu.artic.navigation.NavigationConstants
import edu.artic.viewmodel.BaseViewModelFragment
import edu.artic.viewmodel.Navigate
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.android.synthetic.main.fragment_view_all.*
import kotlin.reflect.KClass

/**
* This represents the `event` sub-screen of the ':welcome' module.
*
* It shows titles, descriptions, start/end times, and a simple
* promotional picture of each [event][edu.artic.db.models.ArticEvent]
* in a single-column vertical list.
*
* @see AllEventsAdapter
*/
class AllEventsFragment : BaseViewModelFragment<AllEventsViewModel>() {

override val screenName: ScreenName
Expand All @@ -33,10 +42,6 @@ class AllEventsFragment : BaseViewModelFragment<AllEventsViewModel>() {

override val title = R.string.events

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Expand All @@ -61,7 +66,7 @@ class AllEventsFragment : BaseViewModelFragment<AllEventsViewModel>() {
}
recyclerView.layoutManager = layoutManager
recyclerView.adapter = eventsAdapter
recyclerView.addItemDecoration(AllEventsItemDecoration(view.context, 2, eventsAdapter))
recyclerView.addItemDecoration(AllEventsItemDecoration(2, eventsAdapter))
}

override fun setupBindings(viewModel: AllEventsViewModel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import javax.inject.Inject
/**
* ViewModel for the 'all events' screen.
*
* Each event is given an [AllEventsCellViewModel], and for each set of those that occur
* Each event is given an [EventCellViewModel], and for each set of those that occur
* on the same date there is one [AllEventsCellHeaderViewModel] immediately preceding.
*/
class AllEventsViewModel @Inject constructor(
Expand Down Expand Up @@ -55,10 +55,10 @@ class AllEventsViewModel @Inject constructor(
))
lastHeaderPosition = viewModelList.size - 1
}
viewModelList.add(AllEventsCellViewModel(
viewModelList.add(EventCellViewModel(
viewDisposeBag,
languageSelector,
tour,
languageSelector,
lastHeaderPosition
))
}
Expand Down Expand Up @@ -106,12 +106,16 @@ class AllEventsCellHeaderViewModel(
}
}

class AllEventsCellViewModel(
/**
* ViewModel responsible for building the event summary list.
*/
class EventCellViewModel(
adapterDisposeBag: DisposeBag,
languageSelector: LanguageSelector,
event: ArticEvent,
val headerPosition: Int
languageSelector: LanguageSelector,
val headerPosition: Int = -1
) : AllEventsCellBaseViewModel(adapterDisposeBag, event) {

val eventTitle: Subject<String> = BehaviorSubject.createDefault(event.title)
val eventDescription: Subject<String> = BehaviorSubject.createDefault(event.short_description.orEmpty())
val eventImageUrl: Subject<String> = BehaviorSubject.createDefault(event.imageURL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import kotlinx.android.synthetic.main.cell_all_exhibitions_layout.view.*
* @author Sameer Dhakal (Fuzz)
*/

class AllExhibitionsAdapter : AutoHolderRecyclerViewAdapter<AllExhibitionsCellViewModel>() {
class AllExhibitionsAdapter : AutoHolderRecyclerViewAdapter<ExhibitionCellViewModel>() {

override fun View.onBindView(item: AllExhibitionsCellViewModel, position: Int) {
override fun View.onBindView(item: ExhibitionCellViewModel, position: Int) {
item.exhibitionImageUrl
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Expand Down