From 54ff190b69bf59137622d8e3f773430b8c71237c Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 20 Jun 2019 16:37:33 +0200 Subject: [PATCH] Refactoring of the feed session adapter --- .../android/model/realm/Filter.kt | 18 +-- .../FeedSessionRowRepresentableAdapter.kt | 142 ++++++++++++------ .../android/ui/fragment/CalendarFragment.kt | 3 +- .../android/ui/fragment/FeedFragment.kt | 95 ++++++------ .../android/ui/fragment/StatisticsFragment.kt | 3 +- .../ui/fragment/components/RealmFragment.kt | 7 +- 6 files changed, 166 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt index 25059c97..1065dc46 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt @@ -1,10 +1,7 @@ package net.pokeranalytics.android.model.realm import android.content.Context -import io.realm.Realm -import io.realm.RealmList -import io.realm.RealmObject -import io.realm.RealmResults +import io.realm.* import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey import io.realm.kotlin.where @@ -153,19 +150,22 @@ open class Filter : RealmObject(), RowRepresentable, Editable, Deletable, Counta } } - inline fun results(firstField: String? = null, secondField: String? = null): RealmResults { - + inline fun query(firstField: String? = null, secondField: String? = null): RealmQuery { val realmQuery = realm.where() if (firstField != null && secondField != null) { - return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll() + return this.query.queryWith(realmQuery).distinct(firstField, secondField) } if (firstField != null) { - return this.query.queryWith(realmQuery).distinct(firstField).findAll() + return this.query.queryWith(realmQuery).distinct(firstField) } - return this.query.queryWith(realmQuery).findAll() + return this.query.queryWith(realmQuery) + } + + inline fun results(firstField: String? = null, secondField: String? = null): RealmResults { + return this.query(firstField, secondField).findAll() } val query: Query diff --git a/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedSessionRowRepresentableAdapter.kt b/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedSessionRowRepresentableAdapter.kt index 535958fe..87fe6a05 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedSessionRowRepresentableAdapter.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedSessionRowRepresentableAdapter.kt @@ -6,12 +6,17 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.AppCompatTextView import androidx.recyclerview.widget.RecyclerView +import io.realm.Realm +import io.realm.RealmQuery import io.realm.RealmResults +import io.realm.Sort +import io.realm.kotlin.where import kotlinx.android.synthetic.main.row_feed_session.view.* +import net.pokeranalytics.android.exceptions.PAIllegalStateException +import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.view.BindableHolder import net.pokeranalytics.android.ui.view.RowViewType -import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.extensions.getMonthAndYear import timber.log.Timber import java.util.* @@ -20,24 +25,70 @@ import kotlin.collections.HashMap /** * An adapter capable of displaying a list of RowRepresentables - * @param dataSource the datasource providing rows - * @param delegate the delegate, notified of UI actions + * The [delegate] is an object notified of UI actions */ class FeedSessionRowRepresentableAdapter( - var delegate: RowRepresentableDelegate? = null, - var realmResults: RealmResults, - var pendingRealmResults: RealmResults, - var distinctHeaders: RealmResults + private var realm: Realm, + var delegate: RowRepresentableDelegate? = null ) : RecyclerView.Adapter() { - private var headersPositions = HashMap() + private lateinit var startedSessions: RealmResults + private lateinit var pendingSessions: RealmResults + private lateinit var sortedHeaders: SortedMap + private var allSessions = mutableListOf() + + + var filter: Filter? = null + set(value) { + field = value + filterChanged() + refreshData() + } + init { + defineSessions() // all sessions refreshData() } + private fun defineSessions() { + + this.startedSessions = requestNewQuery().isNotNull("startDate").findAll() + .sort("startDate", Sort.DESCENDING) + this.pendingSessions = requestNewQuery().isNull("startDate").findAll() + .sort("creationDate", Sort.DESCENDING) + +// Timber.d(">>> startedSessions count = ${startedSessions.size}") +// Timber.d(">>> pendingSessions count = ${pendingSessions.size}") + + // listeners + this.startedSessions.addChangeListener { _, _ -> + refreshData() + } + this.pendingSessions.addChangeListener { _, _ -> + refreshData() + } + + } + + private fun requestNewQuery() : RealmQuery { + this.filter?.let { + return it.query() + } ?: run { + return realm.where() + } + } + + private fun filterChanged() { + + this.startedSessions.removeAllChangeListeners() + this.pendingSessions.removeAllChangeListeners() + + defineSessions() + } + /** * Display a session view */ @@ -88,7 +139,7 @@ class FeedSessionRowRepresentableAdapter( } override fun getItemCount(): Int { - return realmResults.size + pendingRealmResults.size + distinctHeaders.size + return allSessions.size + sortedHeaders.size } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { @@ -110,40 +161,36 @@ class FeedSessionRowRepresentableAdapter( context.getString(net.pokeranalytics.android.R.string.pending) } else { // Else, return the formatted date - val realmHeaderPosition = if (pendingRealmResults.size > 0) sortedHeaders.keys.indexOf(position) - 1 else sortedHeaders.keys.indexOf(position) - distinctHeaders[realmHeaderPosition]?.startDate?.getMonthAndYear() ?: "" + sortedHeaders[position]?.getMonthAndYear() ?: throw PAIllegalStateException("Null date should not happen there") + +// val realmHeaderPosition = sortedHeaders.keys.indexOf(position) +// distinctHeaders[realmHeaderPosition]?.startDate?.getMonthAndYear() ?: throw PAIllegalStateException("Null date should not happen there") } } - return NULL_TEXT + throw PAIllegalStateException("Any position should always have a header, position = $position") +// return NULL_TEXT } fun sessionIdForPosition(position: Int): String? { - val session = this.getSessionForPosition(position) - return session?.id + return this.getSessionForPosition(position)?.id } /** * Get real index */ private fun getSessionForPosition(position: Int): Session? { - return if (pendingRealmResults.size > 0 && position < pendingRealmResults.size + 1) { - // If we have pending session & the position is between these sessions - pendingRealmResults[position - 1] - } else { - // Else, return the correct session - - // Row position - var headersBefore = 0 - for (key in sortedHeaders.keys) { - if (position > key) { - headersBefore++ - } else { - break - } - } - realmResults[position - headersBefore - pendingRealmResults.size] + // Row position + var headersBefore = 0 + for (key in sortedHeaders.keys) { + if (position > key) { + headersBefore++ + } else { + break + } } +// Timber.d("getSessionForPosition = ${position}, headersBefore = $headersBefore") + return allSessions[position - headersBefore] } /** @@ -151,12 +198,15 @@ class FeedSessionRowRepresentableAdapter( */ fun refreshData() { - headersPositions.clear() + allSessions.clear() + allSessions.addAll(this.pendingSessions) + allSessions.addAll(this.startedSessions) - // If we have pending sessions, set the first header to null - if (pendingRealmResults.size > 0) { - headersPositions[0] = null - } +// allSessions.forEach { +// Timber.d(">>> startdate = ${it.startDate}, creationDate = ${it.creationDate}") +// } + + val headersPositions = HashMap() val start = System.currentTimeMillis() @@ -166,12 +216,20 @@ class FeedSessionRowRepresentableAdapter( val calendar = Calendar.getInstance() // Add headers if the date doesn't exist yet - for ((index, session) in realmResults.withIndex()) { - calendar.time = session.startDate ?: session.creationDate - if (checkHeaderCondition(calendar, previousYear, previousMonth)) { - headersPositions[index + headersPositions.size + pendingRealmResults.size] = session.startDate ?: session.creationDate - previousYear = calendar.get(Calendar.YEAR) - previousMonth = calendar.get(Calendar.MONTH) + for ((index, session) in allSessions.withIndex()) { +// Timber.d("/// $index > date = ${session.startDate}") + + val startDate = session.startDate + if (startDate == null) { + headersPositions[0] = null + } else { + calendar.time = startDate + if (checkHeaderCondition(calendar, previousYear, previousMonth)) { +// Timber.d("ADDS HEADER for position= ${index + headersPositions.size}, date = ${session.startDate}") + headersPositions[index + headersPositions.size] = startDate + previousYear = calendar.get(Calendar.YEAR) + previousMonth = calendar.get(Calendar.MONTH) + } } } @@ -183,7 +241,7 @@ class FeedSessionRowRepresentableAdapter( /** * Check if we need to add a header - * Can be change to manage different condition + * Can be changed 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) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt index e74e97bb..957e5baf 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt @@ -8,6 +8,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.tabs.TabLayout import io.realm.Realm import io.realm.RealmModel +import io.realm.RealmResults import kotlinx.android.synthetic.main.fragment_calendar.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -117,7 +118,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable override val observedEntities: List> = listOf(ComputableResult::class.java) - override fun entitiesChanged(clazz: Class) { + override fun entitiesChanged(clazz: Class, results: RealmResults) { launchStatComputation() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt index 104792b4..81d7e256 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt @@ -59,7 +59,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private lateinit var feedSessionAdapter: FeedSessionRowRepresentableAdapter private lateinit var feedTransactionAdapter: FeedTransactionRowRepresentableAdapter - private lateinit var realmSessions: RealmResults + +// private lateinit var realmSessions: RealmResults private lateinit var realmTransactions: RealmResults private lateinit var betaLimitDate: Date @@ -68,14 +69,23 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { private var selectedTransaction: Transaction? = null private var selectedTransactionPosition: Int = -1 - override val observedEntities: List> = listOf(Session::class.java, Transaction::class.java) + override val observedEntities: List> = listOf(Transaction::class.java) + + override fun entitiesChanged(clazz: Class, results: RealmResults) { + super.entitiesChanged(clazz, results) + Timber.d("=== results count = ${results.size}") - override fun entitiesChanged(clazz: Class) { - super.entitiesChanged(clazz) + results.forEach { model -> + if (model is Session) { + Timber.d("++++ sd = ${model.startDate}, year = ${model.year}, month=${model.month}") + } else { + Timber.d(model.toString()) + } + } when (clazz.kotlin) { Session::class -> { - this.feedSessionAdapter.refreshData() +// this.feedSessionAdapter.refreshData() } Transaction::class -> { this.feedTransactionAdapter.refreshData() @@ -112,7 +122,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { throw PAIllegalStateException("Session not found for duplicate at position: ${info.position}") } } - else -> { } + else -> { + } } return true @@ -150,7 +161,6 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { override fun onDestroyView() { super.onDestroyView() - realmSessions.removeAllChangeListeners() realmTransactions.removeAllChangeListeners() } @@ -187,6 +197,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { */ private fun initUI() { + this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(getRealm(), this) + registerForContextMenu(this.recyclerView) val messageToShow: Preferences.FeedMessage? = Preferences.feedMessageToShow(requireContext()) @@ -245,7 +257,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { val sdf = SimpleDateFormat("dd/M/yyyy hh:mm", Locale.getDefault()) betaLimitDate = sdf.parse("17/7/2019 10:00") - this.currentFilterable = FilterableType.SESSION + this.currentFilterable = FilterableType.SESSION val viewManager = SmoothScrollLinearLayoutManager(requireContext()) recyclerView.apply { @@ -257,30 +269,15 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { } private fun loadSessions(filter: Filter? = null) { - val sessionFilter: Filter? = filter?.let { - if (it.filterableType == FilterableType.SESSION) { - it - } else { - null - } - } - // Sessions - this.realmSessions = - sessionFilter?.results() ?: run { getRealm().where().isNotNull("startDate").findAll() } - this.realmSessions = this.realmSessions.sort("startDate", Sort.DESCENDING) - - val pendingSessions = sessionFilter?.let { - getRealm().where().alwaysFalse().findAll() - } ?: run { - getRealm().where().isNull("year").isNull("month").findAll().sort("startDate", Sort.DESCENDING) - } - var distinctDateSessions = sessionFilter?.results("year", "month") ?: run { - getRealm().where().distinct("year", "month").findAll() + when (filter?.filterableType) { + FilterableType.SESSION -> { + this.feedSessionAdapter.filter = filter + } + else -> { + this.feedSessionAdapter.filter = null + } } - distinctDateSessions = distinctDateSessions.sort("startDate", Sort.DESCENDING) - this.feedSessionAdapter = - FeedSessionRowRepresentableAdapter(this, realmSessions, pendingSessions, distinctDateSessions) } private fun loadTransactions(filter: Filter? = null) { @@ -323,7 +320,13 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { return } - SessionActivity.newInstanceforResult(this, isTournament, sessionId = sessionId, duplicate = duplicate, requestCode = RequestCode.NEW_SESSION.value) + SessionActivity.newInstanceforResult( + this, + isTournament, + sessionId = sessionId, + duplicate = duplicate, + requestCode = RequestCode.NEW_SESSION.value + ) newSessionCreated = true } @@ -344,9 +347,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { * Delete selected transaction */ private fun deleteSelectedTransaction() { - getRealm().executeTransaction { - selectedTransaction?.deleteFromRealm() - } + getRealm().executeTransaction { + selectedTransaction?.deleteFromRealm() + } selectedTransactionPosition = -1 } @@ -372,18 +375,18 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate { this.loadTransactions(filter) filter?.let { - when (it.filterableType) { - FilterableType.SESSION -> { - recyclerView.adapter = feedSessionAdapter - this.selectTab(Tab.SESSIONS) - } - FilterableType.TRANSACTION -> { - recyclerView.adapter = feedTransactionAdapter - this.selectTab(Tab.TRANSACTIONS) - } - else -> { - } - } + when (it.filterableType) { + FilterableType.SESSION -> { + recyclerView.adapter = feedSessionAdapter + this.selectTab(Tab.SESSIONS) + } + FilterableType.TRANSACTION -> { + recyclerView.adapter = feedTransactionAdapter + this.selectTab(Tab.TRANSACTIONS) + } + else -> { + } + } adapterHasBeenSet = true } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt index 18d92fdd..f2d83208 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import io.realm.Realm import io.realm.RealmModel +import io.realm.RealmResults import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async @@ -74,7 +75,7 @@ class StatisticsFragment : FilterableFragment() { override val observedEntities: List> = listOf(ComputableResult::class.java) - override fun entitiesChanged(clazz: Class) { + override fun entitiesChanged(clazz: Class, results: RealmResults) { this.launchStatComputation() } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt index 8c583927..fc818918 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import io.realm.Realm import io.realm.RealmModel import io.realm.RealmResults +import timber.log.Timber open class RealmFragment : PokerAnalyticsFragment() { @@ -26,8 +27,8 @@ open class RealmFragment : PokerAnalyticsFragment() { this.observedEntities.forEach { val realmResults = realm.where(it).findAll() - realmResults.addChangeListener { _,_ -> - this.entitiesChanged(it) + realmResults.addChangeListener { t, _ -> + this.entitiesChanged(it, t) } this.observedRealmResults.add(realmResults) @@ -61,6 +62,6 @@ open class RealmFragment : PokerAnalyticsFragment() { /** * The method called when a change happened in any RealmResults */ - open fun entitiesChanged(clazz: Class) {} + open fun entitiesChanged(clazz: Class, results: RealmResults) {} } \ No newline at end of file