parent
565223ad5a
commit
3c81d9dc34
@ -0,0 +1,202 @@ |
|||||||
|
package net.pokeranalytics.android.ui.adapter |
||||||
|
|
||||||
|
import android.view.LayoutInflater |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import androidx.appcompat.widget.AppCompatTextView |
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import io.realm.RealmModel |
||||||
|
import io.realm.RealmQuery |
||||||
|
import io.realm.RealmResults |
||||||
|
import net.pokeranalytics.android.R |
||||||
|
import net.pokeranalytics.android.exceptions.PAIllegalStateException |
||||||
|
import net.pokeranalytics.android.model.realm.Filter |
||||||
|
import net.pokeranalytics.android.ui.view.RowRepresentable |
||||||
|
import net.pokeranalytics.android.ui.view.RowViewType |
||||||
|
import net.pokeranalytics.android.util.NULL_TEXT |
||||||
|
import net.pokeranalytics.android.util.extensions.getMonthAndYear |
||||||
|
import java.util.* |
||||||
|
import kotlin.collections.HashMap |
||||||
|
|
||||||
|
interface DateModel : RowRepresentable, RealmModel { |
||||||
|
var date: Date |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
interface EntityDescriptor<T : DateModel> { |
||||||
|
|
||||||
|
fun bindableHolder(view: View): RecyclerView.ViewHolder |
||||||
|
|
||||||
|
val layout: Int |
||||||
|
|
||||||
|
val viewType: Int |
||||||
|
|
||||||
|
val sortFieldName: String |
||||||
|
|
||||||
|
fun distinctHeaders(realmQuery: RealmQuery<T>): RealmQuery<T> |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* An adapter capable of displaying a list of RowRepresentables |
||||||
|
* @param dataSource the datasource providing rows |
||||||
|
* @param delegate the delegate, notified of UI actions |
||||||
|
*/ |
||||||
|
class FilterSectionAdapter<T : DateModel>( |
||||||
|
override var delegate: RowRepresentableDelegate? = null, |
||||||
|
override var dataSource: RowRepresentableDataSource, |
||||||
|
var descriptor: EntityDescriptor<T>, |
||||||
|
var realmQuery: RealmQuery<T> |
||||||
|
// var distinctTransactionsHeaders: RealmResults<Transaction> |
||||||
|
) : |
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>(), RecyclerAdapter { |
||||||
|
|
||||||
|
private var headersPositions = HashMap<Int, Date?>() |
||||||
|
private lateinit var sortedHeaders: SortedMap<Int, Date?> |
||||||
|
|
||||||
|
private var realmEntities: RealmResults<T> |
||||||
|
private var realmHeaders: RealmResults<T> |
||||||
|
|
||||||
|
var filter: Filter? = null |
||||||
|
|
||||||
|
init { |
||||||
|
|
||||||
|
|
||||||
|
this.realmEntities = this.realmQuery.findAll().sort(this.descriptor.sortFieldName) |
||||||
|
// this.realmEntities = this.filter?.results() ?: this.realmQuery.findAll().sort(this.descriptor.sortFieldName) |
||||||
|
this.realmHeaders = this.descriptor.distinctHeaders(this.realmQuery).findAll() |
||||||
|
|
||||||
|
|
||||||
|
refreshData() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Display a transaction view |
||||||
|
*/ |
||||||
|
inner class RowEntityViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { |
||||||
|
|
||||||
|
fun bind(position: Int, row: T?, adapter: FilterSectionAdapter<T>) { |
||||||
|
|
||||||
|
// itemView.transactionRow.setData(row as Transaction) |
||||||
|
// val listener = View.OnClickListener { |
||||||
|
// adapter.delegate?.onRowSelected(position, row) |
||||||
|
// } |
||||||
|
// itemView.transactionRow.setOnClickListener(listener) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Display a header |
||||||
|
*/ |
||||||
|
inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { |
||||||
|
|
||||||
|
fun bind(title: String) { |
||||||
|
// Title |
||||||
|
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { |
||||||
|
it.text = title |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { |
||||||
|
val inflater = LayoutInflater.from(parent.context) |
||||||
|
return if (viewType == this.descriptor.viewType) { // TODO layout |
||||||
|
val layout = inflater.inflate(this.descriptor.layout, parent, false) |
||||||
|
this.descriptor.bindableHolder(layout) |
||||||
|
// RowEntityViewHolder(layout) |
||||||
|
} else { |
||||||
|
val layout = inflater.inflate(R.layout.row_header_title, parent, false) |
||||||
|
HeaderTitleViewHolder(layout) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int { |
||||||
|
return if (sortedHeaders.containsKey(position)) { |
||||||
|
RowViewType.HEADER_TITLE.ordinal |
||||||
|
} else { |
||||||
|
this.descriptor.viewType // RowViewType.ROW_TRANSACTION.ordinal |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
override fun getItemCount(): Int { |
||||||
|
return realmEntities.size + realmHeaders.size |
||||||
|
} |
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { |
||||||
|
|
||||||
|
if (holder is BindableHolder) { |
||||||
|
holder.onBind(position, getEntityForPosition(position), this) |
||||||
|
} else if (holder is FeedTransactionRowRepresentableAdapter.HeaderTitleViewHolder) { |
||||||
|
holder.bind(getHeaderForPosition(position)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Return the header |
||||||
|
*/ |
||||||
|
private fun getHeaderForPosition(position: Int): String { |
||||||
|
if (sortedHeaders.containsKey(position)) { |
||||||
|
val realmHeaderPosition = sortedHeaders.keys.indexOf(position) |
||||||
|
return realmHeaders[realmHeaderPosition]?.date?.getMonthAndYear() ?: "" |
||||||
|
} |
||||||
|
return NULL_TEXT |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get real index |
||||||
|
*/ |
||||||
|
private fun getEntityForPosition(position: Int): T { |
||||||
|
|
||||||
|
// Row position |
||||||
|
var headersBefore = 0 |
||||||
|
for (key in sortedHeaders.keys) { |
||||||
|
if (position > key) { |
||||||
|
headersBefore++ |
||||||
|
} else { |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return realmEntities[position - headersBefore] ?: throw PAIllegalStateException("Nasty problem!") |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Refresh headers positions |
||||||
|
*/ |
||||||
|
fun refreshData() { |
||||||
|
|
||||||
|
headersPositions.clear() |
||||||
|
|
||||||
|
var previousYear = Int.MAX_VALUE |
||||||
|
var previousMonth = Int.MAX_VALUE |
||||||
|
|
||||||
|
val calendar = Calendar.getInstance() |
||||||
|
|
||||||
|
// Add headers if the date doesn't exist yet |
||||||
|
for ((index, transaction) in realmEntities.withIndex()) { |
||||||
|
calendar.time = transaction.date |
||||||
|
if (checkHeaderCondition(calendar, previousYear, previousMonth)) { |
||||||
|
headersPositions[index + headersPositions.size] = transaction.date |
||||||
|
previousYear = calendar.get(Calendar.YEAR) |
||||||
|
previousMonth = calendar.get(Calendar.MONTH) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sortedHeaders = headersPositions.toSortedMap() |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if we need to add a header |
||||||
|
* Can be change to manage different condition |
||||||
|
*/ |
||||||
|
private fun checkHeaderCondition(currentCalendar: Calendar, previousYear: Int, previousMonth: Int): Boolean { |
||||||
|
return currentCalendar.get(Calendar.YEAR) == previousYear && currentCalendar.get(Calendar.MONTH) < previousMonth || (currentCalendar.get(Calendar.YEAR) < previousYear) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,2 +0,0 @@ |
|||||||
package net.pokeranalytics.android.ui.adapter |
|
||||||
|
|
||||||
Loading…
Reference in new issue