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

[(∩`-´)⊃━☆゚.*・。゚] Payment methods recycler animations #377

Merged
merged 1 commit into from
Nov 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.support.v7.util.DiffUtil
import android.support.v7.widget.LinearLayoutManager
import com.kickstarter.R
import com.kickstarter.extensions.showConfirmationSnackbar
Expand Down Expand Up @@ -72,7 +73,16 @@ class PaymentMethodsSettingsActivity : BaseActivity<PaymentMethodsViewModel.View
private fun setCards(cards: MutableList<UserPaymentsQuery.Node>) = this.adapter.populateCards(cards)

private fun setUpRecyclerView() {
this.adapter = PaymentMethodsAdapter(this.viewModel)
this.adapter = PaymentMethodsAdapter(this.viewModel, object: DiffUtil.ItemCallback<Any>() {
override fun areItemsTheSame(oldItem: Any?, newItem: Any?): Boolean {
return (oldItem as UserPaymentsQuery.Node).id() == (newItem as UserPaymentsQuery.Node).id()
}

override fun areContentsTheSame(oldItem: Any?, newItem: Any?): Boolean {
return (oldItem as UserPaymentsQuery.Node).id() == (newItem as UserPaymentsQuery.Node).id()
}

})
recycler_view.adapter = this.adapter
recycler_view.layoutManager = LinearLayoutManager(this)
}
Expand Down
193 changes: 193 additions & 0 deletions app/src/main/java/com/kickstarter/ui/adapters/KSListAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package com.kickstarter.ui.adapters

import android.support.annotation.LayoutRes
import android.support.v7.recyclerview.extensions.ListAdapter
import android.support.v7.util.DiffUtil
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.crashlytics.android.Crashlytics
import com.kickstarter.BuildConfig
import com.kickstarter.libs.utils.ExceptionUtils
import com.kickstarter.ui.viewholders.KSViewHolder
import com.trello.rxlifecycle.ActivityEvent

abstract class KSListAdapter(diffUtil: DiffUtil.ItemCallback<Any>) : ListAdapter<Any, KSViewHolder>(diffUtil) {
private val sections = ArrayList<List<Any>>()

fun sections(): List<List<Any>> {
return this.sections
}

fun clearSections() {
this.sections.clear()
}

fun <T> addSection(section: List<T>) {
this.sections.add(ArrayList<Any>(section))
}

fun <T> addSections(sections: List<List<T>>) {
for (section in sections) {
addSection(section)
}
}

protected fun items(): MutableList<Any> {
val items = ArrayList<Any>()
for (section in sections) {
items.addAll(section)
}

return items
}

fun <T> setSection(location: Int, section: List<T>) {
this.sections[location] = ArrayList<Any>(section)
}

fun <T> insertSection(location: Int, section: List<T>) {
this.sections.add(location, ArrayList<Any>(section))
}

/**
* Fetch the layout id associated with a sectionRow.
*/
protected abstract fun layout(sectionRow: SectionRow): Int

/**
* Returns a new KSViewHolder given a layout and view.
*/
protected abstract fun viewHolder(@LayoutRes layout: Int, view: View): KSViewHolder

override fun onViewDetachedFromWindow(holder: KSViewHolder) {
super.onViewDetachedFromWindow(holder)

// View holders are "stopped" when they are detached from the window for recycling
holder.lifecycleEvent(ActivityEvent.STOP)

// View holders are "destroy" when they are detached from the window and no adapter is listening
// to events, so ostensibly the view holder is being deallocated.
if (!hasObservers()) {
holder.lifecycleEvent(ActivityEvent.DESTROY)
}
}

override fun onViewAttachedToWindow(holder: KSViewHolder) {
super.onViewAttachedToWindow(holder)

// View holders are "started" when they are attached to the new window because this means
// it has been recycled.
holder.lifecycleEvent(ActivityEvent.START)
}

override fun onCreateViewHolder(viewGroup: ViewGroup, @LayoutRes layout: Int): KSViewHolder {
val view = inflateView(viewGroup, layout)
val viewHolder = viewHolder(layout, view)

viewHolder.lifecycleEvent(ActivityEvent.CREATE)

return viewHolder
}

override fun onBindViewHolder(viewHolder: KSViewHolder, position: Int) {
val data = objectFromPosition(position)

try {
viewHolder.bindData(data)
} catch (e: Exception) {
if (BuildConfig.DEBUG) {
ExceptionUtils.rethrowAsRuntimeException(e)
} else {
// TODO: alter the exception message to say we are just reporting it and it's not a real crash.
Crashlytics.logException(e)
}
}

}

override fun getItemViewType(position: Int): Int {
return layout(sectionRowFromPosition(position))
}

override fun getItemCount(): Int {
return items().size
}

/**
* Gets the data object associated with a sectionRow.
*/
protected fun objectFromSectionRow(sectionRow: SectionRow): Any {
return this.sections[sectionRow.section()][sectionRow.row()]
}

protected fun sectionCount(section: Int): Int {
return if (section > sections().size - 1) {
0
} else sections()[section].size
}

/**
* Gets the data object associated with a position.
*/
protected fun objectFromPosition(position: Int): Any {
return objectFromSectionRow(sectionRowFromPosition(position))
}

private fun sectionRowFromPosition(position: Int): SectionRow {
val sectionRow = SectionRow()
eoji marked this conversation as resolved.
Show resolved Hide resolved
var cursor = 0
for (section in this.sections) {
for (item in section) {
if (cursor == position) {
return sectionRow
}
cursor++
sectionRow.nextRow()
}
sectionRow.nextSection()
}

throw RuntimeException("Position $position not found in sections")
}

private fun inflateView(viewGroup: ViewGroup, @LayoutRes viewType: Int): View {
val layoutInflater = LayoutInflater.from(viewGroup.context)
return layoutInflater.inflate(viewType, viewGroup, false)
}

/**
* SectionRows allow RecyclerViews to be structured into sections of rows.
*/
protected inner class SectionRow {
private var section: Int = 0
private var row: Int = 0

constructor() {
this.section = 0
this.row = 0
}

constructor(section: Int, row: Int) {
this.section = section
this.row = row
}

fun section(): Int {
return this.section
}

fun row(): Int {
return this.row
}

fun nextRow() {
this.row++
}

fun nextSection() {
this.section++
this.row = 0
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.kickstarter.ui.adapters

import UserPaymentsQuery
import android.support.v7.util.DiffUtil
import android.view.View
import com.kickstarter.R
import com.kickstarter.ui.viewholders.KSViewHolder
import com.kickstarter.ui.viewholders.PaymentMethodsViewHolder

class PaymentMethodsAdapter(private val delegate: PaymentMethodsViewHolder.Delegate): KSAdapter() {
const val SECTION_CARDS = 0

class PaymentMethodsAdapter(private val delegate: PaymentMethodsViewHolder.Delegate, diffCallback: DiffUtil.ItemCallback<Any>): KSListAdapter(diffCallback) {

init {
addSection(emptyList<Any>())
Expand All @@ -19,7 +22,7 @@ class PaymentMethodsAdapter(private val delegate: PaymentMethodsViewHolder.Deleg
override fun viewHolder(layout: Int, view: View): KSViewHolder = PaymentMethodsViewHolder(view, delegate)

fun populateCards(cards: MutableList<UserPaymentsQuery.Node>) {
setSection(0, cards)
notifyDataSetChanged()
setSection(SECTION_CARDS, cards)
submitList(items())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ interface PaymentMethodsViewModel {
}

override fun deleteCardButtonClicked(paymentMethodsViewHolder: PaymentMethodsViewHolder, paymentSourceId: String) {
return this.deleteCardClicked(paymentSourceId)
deleteCardClicked(paymentSourceId)
}

override fun confirmDeleteCardClicked() = this.confirmDeleteCardClicked.onNext(null)
Expand Down