diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt index 7f347618..2fd4b380 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/DateFilterInstrumentedUnitTest.kt @@ -8,6 +8,7 @@ import net.pokeranalytics.android.model.realm.FilterCondition import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow +import net.pokeranalytics.android.util.extensions.startOfDay import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith @@ -150,6 +151,223 @@ class DateFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() { } } + @Test + fun testTodayFilter() { + + val realm = this.mockRealm + realm.beginTransaction() + + val s1 = Session.testInstance(100.0, false) + + val cal = Calendar.getInstance() + cal.time = Date() + cal.add(Calendar.HOUR_OF_DAY, -72) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.TODAY)) + + Assert.assertEquals(1, sessions.size) + sessions[0]?.run { + Assert.assertEquals(s1.id, this.id) + } + } + + @Test + fun testTodayNoonFilter() { + + val realm = this.mockRealm + realm.beginTransaction() + + val cal = Calendar.getInstance() + cal.time = Date().startOfDay() + val s1 = Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.HOUR_OF_DAY, -72) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.TODAY)) + + Assert.assertEquals(1, sessions.size) + sessions[0]?.run { + Assert.assertEquals(s1.id, this.id) + } + } + + + @Test + fun testYesterdayFilter() { + + val realm = this.mockRealm + realm.beginTransaction() + val cal = Calendar.getInstance() + cal.time = Date() + cal.add(Calendar.HOUR_OF_DAY, -24) + val s1 = Session.testInstance(100.0, false, cal.time) + + Session.testInstance(100.0, false) + + cal.add(Calendar.HOUR_OF_DAY, -72) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.YESTERDAY)) + + Assert.assertEquals(1, sessions.size) + sessions[0]?.run { + Assert.assertEquals(s1.id, this.id) + } + } + + @Test + fun testYesterdayNoonFilter() { + + val realm = this.mockRealm + realm.beginTransaction() + val cal = Calendar.getInstance() + cal.time = Date() + cal.add(Calendar.HOUR_OF_DAY, -24) + val s1 = Session.testInstance(100.0, false, cal.time.startOfDay()) + + Session.testInstance(100.0, false) + + cal.add(Calendar.HOUR_OF_DAY, -72) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.YESTERDAY)) + + Assert.assertEquals(1, sessions.size) + sessions[0]?.run { + Assert.assertEquals(s1.id, this.id) + } + } + + @Test + fun testTodayAndYesterdayFilter() { + + val realm = this.mockRealm + realm.beginTransaction() + val cal = Calendar.getInstance() + cal.time = Date() + cal.add(Calendar.HOUR_OF_DAY, -24) + val s1 = Session.testInstance(100.0, false, cal.time) + + val s2 = Session.testInstance(100.0, false) + + cal.add(Calendar.HOUR_OF_DAY, -72) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 3) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.TODAY_AND_YESTERDAY)) + + Assert.assertEquals(2, sessions.size) + Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2))) + } + + @Test + fun testThisYear() { + + val realm = this.mockRealm + realm.beginTransaction() + val s1 = Session.testInstance(100.0, false) + + val cal = Calendar.getInstance() + cal.time = Date() + cal.add(Calendar.HOUR_OF_DAY, -24) + val s2 = Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.HOUR_OF_DAY, -72) + val s3 = Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.YEAR, -4) + Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.YEAR, -1) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 5) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.THIS_YEAR)) + + Assert.assertEquals(3, sessions.size) + Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2, s3))) + } + + @Test + fun testThisMonth() { + + val realm = this.mockRealm + realm.beginTransaction() + val s1 = Session.testInstance(100.0, false) + + val cal = Calendar.getInstance() + cal.time = Date() + cal.set(Calendar.DAY_OF_MONTH, 1) + cal.time = cal.time.startOfDay() + val s2 = Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.HOUR_OF_DAY, -1) + Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.MONTH, -1) + Session.testInstance(100.0, false, cal.time) + + cal.add(Calendar.YEAR, -1) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 5) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.THIS_MONTH)) + + Assert.assertEquals(2, sessions.size) + Assert.assertTrue(sessions.containsAll(arrayListOf(s1,s2))) + } + + @Test + fun testThisWeek() { + + val realm = this.mockRealm + realm.beginTransaction() + val s1 = Session.testInstance(100.0, false) + + val cal = Calendar.getInstance() + cal.time = Date() + cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1) + cal.time = cal.time.startOfDay() + cal.add(Calendar.HOUR_OF_DAY, -1) + Session.testInstance(100.0, false, cal.time) + + realm.commitTransaction() + + Assert.assertTrue(realm.where(Session::class.java).findAll().count() == 2) + + val sessions = Filter.queryOn(realm, arrayListOf(QueryCondition.THIS_WEEK)) + + Assert.assertEquals(1, sessions.size) + Assert.assertTrue(sessions.containsAll(arrayListOf(s1))) + } + @Test fun testStartedFomDateFilter() { diff --git a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt index 057a0b7d..f1ff2e51 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -17,6 +17,7 @@ sealed class PokerAnalyticsException(message: String) : Exception(message) { object QueryTypeUnhandled: PokerAnalyticsException(message = "filter type not handled") object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected") object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "filter is empty or null") + data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "filter element '$filterElementRow' type is missing") data class QueryValueMapMissingKeys(val missingKeys: List) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys") data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no filter type for $filterElementRow") } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt index 0ad656f7..d0c90f6b 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt @@ -6,11 +6,10 @@ import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.realm.FilterCondition import net.pokeranalytics.android.model.realm.FilterElementBlind import net.pokeranalytics.android.model.realm.Session +import net.pokeranalytics.android.util.extensions.endOfDay +import net.pokeranalytics.android.util.extensions.startOfDay import java.time.* import java.util.* -import java.time.temporal.TemporalQueries.zoneId - - /** @@ -68,6 +67,9 @@ enum class QueryCondition(var operator: Operator? = null) { THIS_WEEK, THIS_MONTH, THIS_YEAR, + PAST_DAYS, + MORE_THAN_DURATION(Operator.MORE), + LESS_THAN_DURATION(Operator.LESS), CURRENCY, CURRENCY_CODE, @@ -242,8 +244,43 @@ enum class QueryCondition(var operator: Operator? = null) { } query.`in`(fieldName, arrayOf(Calendar.SATURDAY, Calendar.SUNDAY)) } - TODAY, YESTERDAY, TODAY_AND_YESTERDAY, THIS_WEEK, THIS_MONTH, THIS_YEAR -> { - realmQuery + TODAY -> { + val startDate = Date() + realmQuery.between(fieldName, startDate.startOfDay(), startDate.endOfDay()) + } + TODAY_AND_YESTERDAY-> { + val startDate = Date() + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar.add(Calendar.HOUR_OF_DAY, -24) + realmQuery.between(fieldName, calendar.time.startOfDay(), startDate.endOfDay()) + } + YESTERDAY -> { + val calendar = Calendar.getInstance() + calendar.time = Date() + calendar.add(Calendar.HOUR_OF_DAY, -24) + realmQuery.between(fieldName, calendar.time.startOfDay(), calendar.time.endOfDay()) + } + THIS_WEEK -> { + val startDate = Date() + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.SUNDAY) + realmQuery.between(fieldName, calendar.time.startOfDay(), startDate.endOfDay()) + } + THIS_MONTH -> { + val startDate = Date() + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar.set(Calendar.DAY_OF_MONTH, 1) + realmQuery.between(fieldName, calendar.time.startOfDay(), startDate.endOfDay()) + } + THIS_YEAR -> { + val startDate = Date() + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar.set(Calendar.DAY_OF_YEAR, 1) + realmQuery.between(fieldName, calendar.time.startOfDay(), startDate.endOfDay()) } else -> { throw PokerAnalyticsException.QueryTypeUnhandled 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 c962b609..1a74dd53 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 @@ -40,6 +40,7 @@ open class Filter : RealmObject() { return realm.where().equalTo("id", filterId).findFirst() } +<<<<<<< HEAD @TestOnly inline fun queryOn(realm: Realm, queries: List): RealmResults { var realmQuery = realm.where() @@ -50,6 +51,17 @@ open class Filter : RealmObject() { return realmQuery.findAll() } } +======= + @TestOnly + inline fun queryOn(realm: Realm, queries: List): RealmResults { + var realmQuery = realm.where() + queries.forEach { + realmQuery = it.filter(realmQuery) + } + return realmQuery.findAll() + } + } +>>>>>>> d92e2a30b3498d0b6d57197c93b188e100151f1c @PrimaryKey var id = UUID.randomUUID().toString() @@ -61,59 +73,88 @@ open class Filter : RealmObject() { // for MutableRealmInteger, see https://realm.io/docs/java/latest/#counters val usageCount: MutableRealmInteger = MutableRealmInteger.valueOf(0) - var filterConditions: RealmList = RealmList() - private set - - fun createOrUpdateFilterConditions(filterConditionRows: ArrayList) { - filterConditions.clear() - filterConditionRows - .map { - it.filterSectionRow - } - .distinct() - .forEach { section -> - filterConditionRows - .filter { - it.filterSectionRow == section - } - .apply { - - if (this.size == 1) { - filterConditions.add(FilterCondition(this.first())) - } else { - val casted = arrayListOf() - casted.addAll(this) - filterConditions.add(FilterCondition(casted)) - } + var filterConditions: RealmList = RealmList() + private set + fun createOrUpdateFilterConditions(filterConditionRows: ArrayList) { + filterConditions.clear() + filterConditionRows + .map { + it.filterSectionRow + } + .distinct() + .forEach { section -> + filterConditionRows + .filter { + it.filterSectionRow == section + } + .apply { + if (this.size == 1) { + filterConditions.add(FilterCondition(this.first())) + } else { + val casted = arrayListOf() + casted.addAll(this) + filterConditions.add(FilterCondition(casted)) + } } } } - fun countBy(filterCategoryRow: FilterCategoryRow) : Int { - val sections = filterCategoryRow.filterSectionRows - return filterConditions.count { - sections.contains(FilterSectionRow.valueOf(it.sectionName ?: throw PokerAnalyticsException.FilterElementUnknownSectionName)) - } - } + fun countBy(filterCategoryRow: FilterCategoryRow): Int { + val sections = filterCategoryRow.filterSectionRows + return filterConditions.count { + sections.contains(FilterSectionRow.valueOf(it.sectionName ?: throw PokerAnalyticsException.FilterElementUnknownSectionName)) + } + } - fun contains(filterElementRow:FilterElementRow) : Boolean { - val filtered = filterConditions.filter { - it.filterName == filterElementRow.filterName - } - if (filtered.isEmpty()) { - return false - } - return filterElementRow.contains(filtered) - } + fun contains(filterElementRow: FilterElementRow): Boolean { + val filtered = filterConditions.filter { + it.filterName == filterElementRow.filterName + } + if (filtered.isEmpty()) { + return false + } + return filterElementRow.contains(filtered) + } + + /** + * Set the saved value in the filter for the given [filterElementRow] + */ + fun setSavedValueForElement(filterElementRow: FilterElementRow) { + when (filterElementRow) { + is FilterElementRow.PastDays -> { + val values = getSavedValueForElement(filterElementRow) as Array<*> + if (values.isNotEmpty() && values.first() is Int) { + filterElementRow.lastDays = values.first() as Int + } + } + is FilterElementRow.From -> filterElementRow.date = getSavedValueForElement(filterElementRow) as Date? ?: Date() + is FilterElementRow.To -> filterElementRow.date = getSavedValueForElement(filterElementRow) as Date? ?: Date() + } + } + + /** + * Get the saved value for the given [filterElementRow] + */ + private fun getSavedValueForElement(filterElementRow: FilterElementRow): Any? { + val filtered = filterConditions.filter { + it.filterName == filterElementRow.filterName + } + + if (filtered.isNotEmpty()) { + return filtered.first().getFilterConditionValue(filterElementRow) + } + + return null + } inline fun results(): RealmResults { - var realmQuery = realm.where() - this.filterConditions.map { - it.queryCondition - }.forEach { - realmQuery = it.filter(realmQuery) - } + var realmQuery = realm.where() + this.filterConditions.map { + it.queryCondition + }.forEach { + realmQuery = it.filter(realmQuery) + } return realmQuery.findAll() } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/FilterCondition.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/FilterCondition.kt index be7aa78c..db893d78 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/FilterCondition.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/FilterCondition.kt @@ -16,6 +16,10 @@ open class FilterCondition() : RealmObject() { } constructor(filterElementRows: ArrayList) : this(filterElementRows.first().filterName, filterElementRows.first().filterSectionRow.name) { + + + + val filterName : String = this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName this.stringValues = when (QueryCondition.valueOf(filterName)) { QueryCondition.GAME, QueryCondition.BANKROLL, QueryCondition.TOURNAMENT_NAME, QueryCondition.ALL_TOURNAMENT_FEATURES, QueryCondition.ANY_TOURNAMENT_FEATURES, QueryCondition.LOCATION -> { @@ -72,6 +76,13 @@ open class FilterCondition() : RealmObject() { }) } } + QueryCondition.PAST_DAYS -> { + RealmList().apply { + this.addAll(filterElementRows.map { + (it as FilterElementRow.PastDays).lastDays.toDouble() + }) + } + } else -> null } @@ -157,4 +168,16 @@ open class FilterCondition() : RealmObject() { val year: Int get() = numericValues?.first()?.toInt() ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing + + /** + * Return the value associated with the given [filterElementRow] + */ + fun getFilterConditionValue(filterElementRow: FilterElementRow): Any? { + return when (filterElementRow) { + is From, is To -> dateValue //TODO: Probably change by 'date' (doesn't work now because the value isn't correctly saved + is PastDays -> values + else -> throw PokerAnalyticsException.FilterElementTypeMissing(filterElementRow) + } + } + } diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index a2bc23e0..076649d4 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -90,6 +90,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat DAY_OF_WEEK, WEEK_END, WEEK_DAY -> "dayOfWeek" MONTH -> "month" YEAR -> "year" + TODAY, YESTERDAY, TODAY_AND_YESTERDAY, THIS_YEAR, THIS_MONTH, THIS_WEEK -> "startDate" else -> null } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt index 84476e86..303333e4 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt @@ -19,13 +19,18 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment +import net.pokeranalytics.android.ui.helpers.DateTimePickerManager import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow +import net.pokeranalytics.android.util.NULL_TEXT +import net.pokeranalytics.android.util.extensions.shortDate +import net.pokeranalytics.android.util.extensions.toMinutes import timber.log.Timber import java.util.* +import kotlin.collections.ArrayList open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { @@ -64,9 +69,24 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta Timber.d("Row: $row") when (row) { - is FilterElementRow.ResultMoreThan -> { - val data = row.editingDescriptors(mapOf("defaultValue" to "")) - BottomSheetFragment.create(fragmentManager, row, this, data, null) + is FilterElementRow.From -> DateTimePickerManager.create(requireContext(), row, this, row.date, onlyDate = true) + is FilterElementRow.To -> DateTimePickerManager.create(requireContext(), row, this, row.date, onlyDate = true) + is FilterElementRow.PastDays -> { + val pastDays = if (row.lastDays > 0) row.lastDays.toString() else "" + val data = row.editingDescriptors(mapOf("pastDays" to pastDays)) + BottomSheetFragment.create(fragmentManager, row, this, data, true) + } + is FilterElementRow.DurationMoreThan -> { + val hours = if (row.minutes / 60 > 0) (row.minutes / 60).toString() else "" + val minutes = if (row.minutes % 60 > 0) (row.minutes % 60).toString() else "" + val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes)) + BottomSheetFragment.create(fragmentManager, row, this, data, true) + } + is FilterElementRow.DurationLessThan -> { + val hours = if (row.minutes / 60 > 0) (row.minutes / 60).toString() else "" + val minutes = if (row.minutes % 60 > 0) (row.minutes % 60).toString() else "" + val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes)) + BottomSheetFragment.create(fragmentManager, row, this, data, true) } else -> { @@ -88,7 +108,6 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta selectedRows.add(row) } } - } } @@ -111,6 +130,16 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta rowRepresentableAdapter.refreshRow(row) } + override fun stringForRow(row: RowRepresentable): String { + return when (row) { + is FilterElementRow.PastDays -> if (row.lastDays > 0) row.lastDays.toString() else NULL_TEXT + is FilterElementRow.From -> row.date.shortDate() + is FilterElementRow.To -> row.date.shortDate() + is FilterElementRow.TimeFilterElementRow -> row.minutes.toMinutes(requireContext()) + else -> super.stringForRow(row) + } + } + override fun isSelected(row: RowRepresentable): Boolean { return selectedRows.contains(row) } @@ -118,7 +147,40 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta override fun onRowValueChanged(value: Any?, row: RowRepresentable) { super.onRowValueChanged(value, row) Timber.d("onRowValueChanged: $row $value") - selectedRows.add(row as FilterElementRow) + + when (row) { + is FilterElementRow.From -> row.date = if (value != null && value is Date) value else Date() + is FilterElementRow.To -> row.date = if (value != null && value is Date) value else Date() + is FilterElementRow.PastDays -> row.lastDays = if (value != null && value is String) value.toInt() else 0 + is FilterElementRow.TimeFilterElementRow -> { + if (value is ArrayList<*>) { + val hours = try { + (value[0] as String? ?: "0").toInt() + } catch (e: Exception) { + 0 + } + val minutes = try { + (value[1] as String? ?: "0").toInt() + } catch (e: Exception) { + 0 + } + + row.minutes = hours * 60 + minutes + } else { + row.minutes = 0 + } + } + } + + //TODO: Update management like in onRowSelected + if (value != null) { + if (!selectedRows.contains(row)) { + selectedRows.add(row as FilterElementRow) + } + } else { + selectedRows.remove(row as FilterElementRow) + } + rowRepresentableAdapter.refreshRow(row) } @@ -155,8 +217,6 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta */ private fun initData() { - Timber.d("initData") - primaryKey?.let { currentFilter = Filter.getFilterBydId(getRealm(), it) } @@ -169,8 +229,13 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta this.rowsForFilterSubcategoryRow.clear() this.rows.addAll(it.filterElements) - this.rows.forEach {element -> + currentFilter?.filterConditions?.forEach { + Timber.d(it.toString()) + } + + this.rows.forEach { element -> if (element is FilterElementRow && currentFilter?.contains(element) == true) { + currentFilter?.setSavedValueForElement(element) this.selectedRows.add(element) } } @@ -187,12 +252,20 @@ open class FilterDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresenta private fun saveData() { //TODO: Save currentFilter details data Timber.d("Save data for filter: ${currentFilter?.id}") + selectedRows?.forEach { + Timber.d("Selected rows: $it") + } val realm = getRealm() realm.beginTransaction() currentFilter?.createOrUpdateFilterConditions(selectedRows) realm.commitTransaction() + currentFilter?.filterConditions?.forEach { + Timber.d("Condition: $it") + } + + finishActivityWithResult(currentFilter?.id) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt index 160e0cc1..919acd05 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt @@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.fragment.components.bottomsheet import android.os.Bundle import android.view.LayoutInflater import android.view.View +import androidx.core.view.get import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.chip.Chip import io.realm.RealmResults @@ -13,6 +14,7 @@ import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorExcep import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.util.extensions.px /** * Bottom Sheet List Game Fragment @@ -73,6 +75,8 @@ class BottomSheetListGameFragment : BottomSheetListFragment() { val chip = Chip(requireContext()) chip.id = it.ordinal chip.text = it.shortName + chip.chipStartPadding = 8f.px + chip.chipEndPadding = 8f.px chip.isChecked = it.ordinal == limit chipGroup.addView(chip) } @@ -81,6 +85,10 @@ class BottomSheetListGameFragment : BottomSheetListFragment() { values[0] = i } + if (limit == null) { + (chipGroup[0] as Chip).isChecked = true + } + val viewManager2 = LinearLayoutManager(requireContext()) dataAdapter = RowRepresentableAdapter(this, this) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt index f183236b..0dd9a3fa 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterElementRow.kt @@ -1,17 +1,23 @@ package net.pokeranalytics.android.ui.view.rowrepresentable import android.content.Context +import android.text.InputType import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.realm.FilterCondition +import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowViewType +import java.text.DateFormatSymbols import java.util.* sealed class FilterElementRow : RowRepresentable { + // Objects + object Cash : FilterElementRow() object Tournament : FilterElementRow() object Live : FilterElementRow() @@ -25,6 +31,15 @@ sealed class FilterElementRow : RowRepresentable { object Weekday : FilterElementRow() object Weekend : FilterElementRow() + object ResultMoreThan : MoreFilterElementRow() + object ResultLessThan : LessFilterElementRow() + object DurationMoreThan : MoreTimeFilterElementRow() + object DurationLessThan : LessTimeFilterElementRow() + + // Subclasses + + open class SingleValueFilterElementRow(val value: Int) : FilterElementRow() + open class DataFilterElementRow(data: Manageable) : FilterElementRow() { val id: String = data.id val name: String = (data as RowRepresentable).getDisplayName() @@ -40,7 +55,17 @@ sealed class FilterElementRow : RowRepresentable { } } - open class SingleValueFilterElementRow(val value: Int) : FilterElementRow() + open class QuantityFilterElementRow(var value: Double = 0.0) : FilterElementRow() + open class TimeFilterElementRow : QuantityFilterElementRow() { + var minutes = value.toInt() + } + + open class MoreFilterElementRow : QuantityFilterElementRow() + open class LessFilterElementRow : QuantityFilterElementRow() + open class MoreTimeFilterElementRow : TimeFilterElementRow() + open class LessTimeFilterElementRow : TimeFilterElementRow() + + // Data classes data class Blind(var sb: Double? = null, var bb: Double? = null, var code: String? = null) : FilterElementRow() data class From(var date: Date = Date()) : FilterElementRow() @@ -57,8 +82,7 @@ sealed class FilterElementRow : RowRepresentable { data class TournamentName(val tournamentName: Manageable) : DataFilterElementRow(tournamentName) data class AllTournamentFeature(val tournamentFeature: Manageable) : DataFilterElementRow(tournamentFeature) data class AnyTournamentFeature(val tournamentFeature: Manageable) : DataFilterElementRow(tournamentFeature) - data class ResultMoreThan(var value: Double) : FilterElementRow() - data class ResultLessThan(var value: Double) : FilterElementRow() + lateinit var filterSectionRow: FilterSectionRow @@ -85,10 +109,7 @@ sealed class FilterElementRow : RowRepresentable { is CurrentWeek -> QueryCondition.THIS_WEEK is CurrentMonth -> QueryCondition.THIS_MONTH is CurrentYear -> QueryCondition.THIS_YEAR - /* - is PastDays -> R.string.period_in_days - */ - + is PastDays -> QueryCondition.PAST_DAYS is Limit -> QueryCondition.LIMIT is TableSize -> QueryCondition.TABLE_SIZE is Game -> QueryCondition.GAME @@ -99,12 +120,18 @@ sealed class FilterElementRow : RowRepresentable { is AllTournamentFeature -> QueryCondition.ALL_TOURNAMENT_FEATURES is ResultMoreThan -> QueryCondition.MORE_THAN_NET_RESULT is ResultLessThan -> QueryCondition.LESS_THAN_NET_RESULT + is DurationMoreThan -> QueryCondition.MORE_THAN_DURATION + is DurationLessThan -> QueryCondition.LESS_THAN_DURATION + else -> throw PokerAnalyticsException.UnknownQueryTypeForRow(this) } } fun contains(filterConditions: List): Boolean { return when (this) { + is SingleValueFilterElementRow -> filterConditions.any { + it.values.contains(this.value) + } is DataFilterElementRow -> filterConditions.any { it.ids.contains(this.id) } @@ -133,13 +160,10 @@ sealed class FilterElementRow : RowRepresentable { is Online -> R.string.online is Weekday -> R.string.week_days is Weekend -> R.string.weekend - is Year -> R.string.year - is Month -> R.string.month_of_the_year - is Day -> R.string.day_of_the_week is PastDays -> R.string.period_in_days is Blind -> R.string.blinds - is ResultMoreThan -> R.string.more_than - is ResultLessThan -> R.string.less_than + is MoreFilterElementRow, is MoreTimeFilterElementRow -> R.string.more_than + is LessFilterElementRow, is LessTimeFilterElementRow -> R.string.less_than else -> null } } @@ -147,13 +171,51 @@ sealed class FilterElementRow : RowRepresentable { override val viewType: Int get() { return when (this) { - is ResultMoreThan -> RowViewType.TITLE_VALUE_CHECK.ordinal + is PastDays, + is From, is To, + is DurationMoreThan, is DurationLessThan -> RowViewType.TITLE_VALUE_CHECK.ordinal else -> RowViewType.TITLE_CHECK.ordinal } } + override val bottomSheetType: BottomSheetType + get() { + return when (this) { + is PastDays -> BottomSheetType.EDIT_TEXT + is DurationMoreThan, is DurationLessThan -> BottomSheetType.DOUBLE_EDIT_TEXT + else -> BottomSheetType.NONE + } + } + + override fun editingDescriptors(map: Map): ArrayList? { + return when (this) { + is PastDays -> { + val pastDays: String? by map + arrayListOf( + RowRepresentableEditDescriptor(pastDays, R.string.period_in_days, inputType = InputType.TYPE_CLASS_NUMBER) + ) + } + is DurationMoreThan, is DurationLessThan -> { + val hours: String? by map + val minutes: String? by map + arrayListOf( + RowRepresentableEditDescriptor(hours, R.string.hour, inputType = InputType.TYPE_CLASS_NUMBER), + RowRepresentableEditDescriptor(minutes, R.string.minute, inputType = InputType.TYPE_CLASS_NUMBER) + ) + } + else -> super.editingDescriptors(map) + } + } + override fun getDisplayName(): String { return when (this) { + is SingleValueFilterElementRow -> { + when (this) { + is Day -> DateFormatSymbols.getInstance(Locale.getDefault()).weekdays[this.value] + is Month -> DateFormatSymbols.getInstance(Locale.getDefault()).months[this.value] + else -> "${this.value}" + } + } is DataFilterElementRow -> this.name is StaticDataFilterElementRow -> this.name else -> super.getDisplayName() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterSectionRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterSectionRow.kt index 19f4ed1d..7158d301 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterSectionRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterSectionRow.kt @@ -8,6 +8,7 @@ import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow.* +import java.text.DateFormatSymbols import java.util.* enum class FilterSectionRow(override val resId: Int?) : RowRepresentable { @@ -91,11 +92,38 @@ enum class FilterSectionRow(override val resId: Int?) : RowRepresentable { CurrentYear ) FIXED_DATE -> arrayListOf(From(), To()) - DURATION -> arrayListOf() + DURATION -> arrayListOf(PastDays()) + YEAR -> { + val years = arrayListOf() + val realm = Realm.getDefaultInstance() + val distinctYears = realm.where().distinct("year").findAll().sort("year", Sort.DESCENDING) + distinctYears.forEach { session -> + session.year?.let { year -> + years.add(Year(year)) + } + } + realm.close() + years + } WEEKDAYS_OR_WEEKEND -> arrayListOf(Weekday, Weekend) - DAY_OF_WEEK -> arrayListOf() - MONTH_OF_YEAR -> arrayListOf() - YEAR -> arrayListOf() + DAY_OF_WEEK -> { + val daysOfWeek = arrayListOf() + DateFormatSymbols.getInstance(Locale.getDefault()).weekdays.forEachIndexed { index, day -> + if (day.isNotEmpty()) { + daysOfWeek.add(Day(index)) + } + } + daysOfWeek + } + MONTH_OF_YEAR -> { + val months = arrayListOf() + DateFormatSymbols.getInstance(Locale.getDefault()).months.forEachIndexed { index, month -> + if (month.isNotEmpty()) { + months.add(Month(index)) + } + } + months + } GAME -> { val games = arrayListOf() @@ -126,7 +154,7 @@ enum class FilterSectionRow(override val resId: Int?) : RowRepresentable { MULTI_PLAYER -> arrayListOf() - SESSION_DURATION -> arrayListOf(ResultMoreThan(0.0), ResultLessThan(0.0)) + SESSION_DURATION -> arrayListOf(DurationMoreThan, DurationLessThan) RANGE -> arrayListOf(From(Date()), To(Date())) VALUE -> arrayListOf() diff --git a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt index 77287988..88330925 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt @@ -1,5 +1,7 @@ package net.pokeranalytics.android.util.extensions +import android.content.Context +import net.pokeranalytics.android.R import java.text.DecimalFormat import java.text.NumberFormat import java.util.* @@ -35,6 +37,23 @@ fun Double.formattedHourlyDuration() : String { return (this * 1000 * 3600).toLong().toMinutes() } +// Return the time from minutes to hours:minutes +fun Int.toMinutes(context: Context) : String { + val hours = this / 60 + val minutesLeft = this % 60 + var duration = "" + + if (hours < 1) { + duration += "$minutesLeft ${context.getString(if (minutesLeft > 1) R.string.mins else R.string.min)}" + } else { + duration += hours.toString() + duration += ":" + duration += if (minutesLeft < 10) "0$minutesLeft" else minutesLeft.toString() + } + + return duration +} + // Return the time from milliseconds to hours:minutes fun Long.toMinutes() : String { val totalMinutes = this / (1000 * 60) @@ -45,5 +64,4 @@ fun Long.toMinutes() : String { duration += ":" duration += if (minutesLeft < 10) "0$minutesLeft" else minutesLeft.toString() return duration - } \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_sheet_game_list.xml b/app/src/main/res/layout/bottom_sheet_game_list.xml index 7f5a61b1..ec47ed64 100644 --- a/app/src/main/res/layout/bottom_sheet_game_list.xml +++ b/app/src/main/res/layout/bottom_sheet_game_list.xml @@ -8,9 +8,9 @@ @@ -22,41 +22,8 @@ android:layout_gravity="center" android:orientation="horizontal" app:chipSpacingHorizontal="16dp" - app:singleSelection="true"> - - - - + app:singleSelection="true" /> + Please set a start date for the session + Hour + Minute