From 7924e19fe3bbdab283deb855fe6424532b6693a2 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 24 Sep 2020 17:05:19 +0200 Subject: [PATCH] Changed QueryCondition single values from optional to mandatory + Details ViewModel creation and refactoring --- .../filter/RealmFilterInstrumentedUnitTest.kt | 4 +- .../calculus/bankroll/BankrollReport.kt | 8 +- .../android/exceptions/Exceptions.kt | 18 +- .../android/model/filter/Query.kt | 16 +- .../android/model/filter/QueryCondition.kt | 277 +++++++--------- .../android/model/realm/Filter.kt | 40 +-- .../android/model/realm/FilterCondition.kt | 10 +- .../modules/filter/FilterDetailsFragment.kt | 302 ++++++++++-------- .../modules/filter/FilterDetailsViewModel.kt | 116 +++++++ .../ui/modules/filter/FilterViewModel.kt | 14 +- .../ui/modules/filter/FiltersActivity.kt | 6 +- .../ui/modules/filter/FiltersFragment.kt | 8 +- .../rowrepresentable/FilterCategoryRow.kt | 8 +- .../view/rowrepresentable/FilterElementRow.kt | 112 ++++++- .../view/rowrepresentable/FilterSectionRow.kt | 182 ++++++----- 15 files changed, 681 insertions(+), 440 deletions(-) create mode 100644 app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsViewModel.kt diff --git a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt index f52f3e2e..022b63ec 100644 --- a/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt +++ b/app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/RealmFilterInstrumentedUnitTest.kt @@ -24,8 +24,8 @@ class RealmFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() { filter.name = "testSaveLoadCashFilter" val filterElement = QueryCondition.IsCash - filterElement.filterSectionRow = FilterSectionRow.CashOrTournament - filter.createOrUpdateFilterConditions(arrayListOf(filterElement)) +// filterElement.filterSectionRow = FilterSectionRow.CashOrTournament + filter.createOrUpdateFilterConditions(arrayListOf(filterElement), FilterSectionRow.CashOrTournament) val useCount = filter.countBy(FilterCategoryRow.GENERAL) Assert.assertEquals(1, useCount) diff --git a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt index ce66fe36..56f0be1d 100644 --- a/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt +++ b/app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt @@ -220,14 +220,10 @@ class BankrollReportSetup(val bankrollId: String? = null, val from: Date? = null query.add(bankrollCondition) } this.from?.let { - val fromCondition = QueryCondition.StartedFromDate() - fromCondition.singleValue = it - query.add(fromCondition) + query.add(QueryCondition.StartedFromDate(it)) } this.to?.let { - val toCondition = QueryCondition.StartedToDate() - toCondition.singleValue = it - query.add(toCondition) + query.add(QueryCondition.StartedToDate(it)) } return query } 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 b12a9342..ce70fd9c 100644 --- a/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt +++ b/app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt @@ -1,7 +1,5 @@ package net.pokeranalytics.android.exceptions -import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow - class ModelException(message: String) : Exception(message) class FormattingException(message: String) : Exception(message) class RowRepresentableEditDescriptorException(message: String) : Exception(message) @@ -14,16 +12,16 @@ class PAIllegalStateException(message: String) : Exception(message) sealed class PokerAnalyticsException(message: String) : Exception(message) { object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition") - object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryCondition") - object FilterMissingEntity: PokerAnalyticsException(message = "This queryWith has no entity initialized") - object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable") +// object FilterElementUnknownSectionName: PokerAnalyticsException(message = "No filterElement section name was found to identify the queryCondition") +// object FilterMissingEntity: PokerAnalyticsException(message = "This queryWith has no entity initialized") +// object FilterUnhandledEntity : PokerAnalyticsException(message = "This entity is not filterable") object QueryValueMapUnknown: PokerAnalyticsException(message = "fieldName is missing") object QueryTypeUnhandled: PokerAnalyticsException(message = "queryWith type not handled") object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected") object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "queryWith is empty or null") - data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith 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 queryWith type for $filterElementRow") - data class MissingFieldNameForQueryCondition(val name: String) : PokerAnalyticsException(message = "Missing fieldname for QueryCondition ${name}") - object InputFragmentException : PokerAnalyticsException(message = "RowEditableDelegate must be a Fragment") +// data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith 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 queryWith type for $filterElementRow") +// data class MissingFieldNameForQueryCondition(val name: String) : PokerAnalyticsException(message = "Missing fieldname for QueryCondition ${name}") +// object InputFragmentException : PokerAnalyticsException(message = "RowEditableDelegate must be a Fragment") } diff --git a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt index d56f03a4..c90b16c0 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt @@ -84,14 +84,14 @@ class Query { } // println("<<<<<< ${realmQuery.description}") - val queryLast = this.conditions.firstOrNull { - it is QueryCondition.Last - } - queryLast?.let {qc -> - (qc as QueryCondition.Last).singleValue?.let { - return realmQuery.limit(it.toLong()) - } - } +// val queryLast = this.conditions.firstOrNull { +// it is QueryCondition.Last +// } +// queryLast?.let {qc -> +// (qc as QueryCondition.Last).singleValue?.let { +// return realmQuery.limit(it.toLong()) +// } +// } return realmQuery } 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 df26b424..f513f38d 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 @@ -16,23 +16,25 @@ import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType +import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType -import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow -import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow +import net.pokeranalytics.android.ui.view.rowrepresentable.* import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.UserDefaults import net.pokeranalytics.android.util.extensions.* +import java.lang.Exception import java.text.DateFormatSymbols import java.text.NumberFormat import java.util.* import kotlin.collections.ArrayList +import kotlin.reflect.full.primaryConstructor /** * Enum describing the way a query should be handled * Some queries requires a value to be checked upon through equals, in, more, less, between */ -sealed class QueryCondition : FilterElementRow { +sealed class QueryCondition : RowRepresentable { companion object { inline fun more(): T { @@ -49,7 +51,15 @@ sealed class QueryCondition : FilterElementRow { fun valueOf(name: String): T { val kClass = Class.forName("${QueryCondition::class.qualifiedName}$$name").kotlin - val instance = kClass.objectInstance ?: kClass.java.newInstance() + val instance = try { + kClass.objectInstance ?: kClass.java.newInstance() + } catch (e: Exception) { + // some object instance can fail due to: java.lang.Class has no zero argument constructor + // We should have just one constructor with a single parameter + val primaryConstructor = kClass.java.declaredConstructors.first() + val param = primaryConstructor.parameterTypes.first() + primaryConstructor.newInstance(param.newInstance()) + } return instance as T } @@ -125,25 +135,29 @@ sealed class QueryCondition : FilterElementRow { abstract class ListOfValues : QueryCondition(), Comparable> where T : Comparable { - abstract var listOfValues: ArrayList + abstract var listOfValues: MutableList abstract fun labelForValue(value: T, context: Context): String open fun entityName(context: Context): String { return getDisplayName(context) } - override fun getDisplayName(context: Context): String { + fun getDisplayName(context: Context, values: List): String { val prefix = this.resId?.let { context.getString(it) + " " } ?: "" - return when (listOfValues.size) { + return when (values.size) { 0 -> return NULL_TEXT - 1, 2 -> prefix + listOfValues.map { labelForValue(it, context) }.joinToString(", ") - else -> "${listOfValues.size} $prefix ${entityName(context)}" + 1, 2 -> prefix + values.joinToString(", ") { labelForValue(it, context) } + else -> "${values.size} $prefix ${entityName(context)}" } } + override fun getDisplayName(context: Context): String { + return getDisplayName(context, this.listOfValues) + } + override fun compareTo(other: ListOfValues): Int { return listOfValues.sorted().first().compareTo(other.listOfValues.sorted().first()) } @@ -151,17 +165,18 @@ sealed class QueryCondition : FilterElementRow { fun firstValue(context: Context): String? { return this.listOfValues.firstOrNull()?.let { this.labelForValue(it, context) } } + } - abstract class SingleValue : ListOfValues() where T : Comparable { - override var listOfValues = ArrayList() - abstract var singleValue: T? + abstract class SingleValue(value: T) : ListOfValues() where T : Comparable { + override var listOfValues = mutableListOf() + var singleValue: T = value } abstract class ListOfDouble : ListOfValues() { open var sign: Int = 1 override var operator: Operator = Operator.ANY - override var listOfValues: ArrayList = arrayListOf() + override var listOfValues = mutableListOf() override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) listOfValues = filterCondition.getValues() @@ -170,11 +185,15 @@ sealed class QueryCondition : FilterElementRow { override fun labelForValue(value: Double, context: Context): String { return value.toCurrency(UserDefaults.currency) } + + override fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return DoubleValueListFilterItemRow(this, filterSectionRow) + } } abstract class ListOfInt : ListOfValues() { override var operator: Operator = Operator.ANY - override var listOfValues: ArrayList = arrayListOf() + override var listOfValues = mutableListOf() override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) listOfValues = filterCondition.getValues() @@ -183,11 +202,15 @@ sealed class QueryCondition : FilterElementRow { override fun labelForValue(value: Int, context: Context): String { return value.toString() } + + override fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return IntValueListFilterItemRow(this, filterSectionRow) + } } abstract class ListOfString : ListOfValues() { override var operator: Operator = Operator.ANY - override var listOfValues = ArrayList() + override var listOfValues = mutableListOf() override fun labelForValue(value: String, context: Context): String { return value } @@ -196,48 +219,42 @@ sealed class QueryCondition : FilterElementRow { super.updateValueBy(filterCondition) listOfValues = filterCondition.getValues() } + + override fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return StringValueListFilterItemRow(this, filterSectionRow) + } } - abstract class SingleDate : SingleValue() { + abstract class SingleDate(date: Date) : SingleValue(date) { override fun labelForValue(value: Date, context: Context): String { return value.shortDate() } - override var listOfValues = ArrayList() - - override var singleValue: Date? - get() { - return listOfValues.firstOrNull() - } - set(value) { - listOfValues.removeAll(this.listOfValues) - value?.let { listOfValues.add(it) } - } + override var listOfValues = mutableListOf() override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) singleValue = filterCondition.getValue() } + + override fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return DateFilterItemRow(this, filterSectionRow) + } } - abstract class SingleInt : SingleValue() { + abstract class SingleInt(value: Int) : SingleValue(value) { override fun labelForValue(value: Int, context: Context): String { return value.toString() } - override var singleValue: Int? - get() { - return listOfValues.firstOrNull() - } - set(value) { - listOfValues.removeAll(this.listOfValues) - value?.let { listOfValues.add(it) } - } - override fun updateValueBy(filterCondition: FilterCondition) { super.updateValueBy(filterCondition) singleValue = filterCondition.getValue() } + + override fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return IntFilterItemRow(this, filterSectionRow) + } } override fun getDisplayName(context: Context): String { @@ -247,7 +264,7 @@ sealed class QueryCondition : FilterElementRow { return baseId } - override var filterSectionRow: FilterSectionRow = FilterSectionRow.CashOrTournament +// override var filterSectionRow: FilterSectionRow = FilterSectionRow.CashOrTournament abstract class QueryDataCondition : ListOfString() { fun setObject(dataObject: T) { @@ -263,7 +280,7 @@ sealed class QueryCondition : FilterElementRow { val completeLabel = when (listOfValues.size) { 0 -> NULL_TEXT 1, 2 -> { - listOfValues.map { labelForValue(realm, it) }.joinToString(", ") + listOfValues.joinToString(", ") { labelForValue(realm, it) } } else -> "${listOfValues.size} $entityName" } @@ -286,21 +303,19 @@ sealed class QueryCondition : FilterElementRow { val showTime: Boolean } - abstract class DateQuery : SingleDate(), DateTime { + abstract class DateQuery(date: Date) : SingleDate(date), DateTime { override val showTime: Boolean = false override fun labelForValue(value: Date, context: Context): String { - return singleValue?.let { - if (showTime) { - it.shortTime() - } else { - it.shortDate() - } - } ?: NULL_TEXT + return if (showTime) { + singleValue.shortTime() + } else { + singleValue.shortDate() + } } } - abstract class TimeQuery : DateQuery() { + abstract class TimeQuery(date: Date) : DateQuery(date) { override val showTime: Boolean = true } @@ -343,50 +358,34 @@ sealed class QueryCondition : FilterElementRow { } } - class AnyTournamentName() : QueryDataCondition() { + class AnyTournamentName : QueryDataCondition() { override val entity: Class = TournamentName::class.java - constructor(tournamentName: TournamentName) : this() { - this.setObject(tournamentName) - } - override fun entityName(context: Context): String { return context.getString(R.string.tournament_names) } } - class AnyTournamentFeature() : QueryDataCondition() { + class AnyTournamentFeature : QueryDataCondition() { override val entity: Class = TournamentFeature::class.java - constructor(tournamentFeature: TournamentFeature) : this() { - this.setObject(tournamentFeature) - } - override fun entityName(context: Context): String { return context.getString(R.string.tournament_features) } } - class AllTournamentFeature() : QueryDataCondition() { + class AllTournamentFeature : QueryDataCondition() { override var operator = Operator.ALL override val entity: Class = TournamentFeature::class.java - constructor(tournamentFeature: TournamentFeature) : this() { - this.setObject(tournamentFeature) - } - override fun entityName(context: Context): String { return context.getString(R.string.tournament_features) } } - class AnyLocation() : QueryDataCondition() { + class AnyLocation : QueryDataCondition() { override val entity: Class = Location::class.java - constructor(location: Location) : this() { - this.setObject(location) - } - override fun entityName(context: Context): String { return context.getString(R.string.locations) } @@ -440,13 +439,13 @@ sealed class QueryCondition : FilterElementRow { } } - object Last : SingleInt() { - override var operator = Operator.EQUALS - override fun getDisplayName(context: Context): String { - //TODO update string "last %i" - return "${context.getString(R.string.last_i_records)} $singleValue" - } - } +// object Last : SingleInt() { +// override var operator = Operator.EQUALS +// override fun getDisplayName(context: Context): String { +// //TODO update string "last %i" +// return "${context.getString(R.string.last_i_records)} $singleValue" +// } +// } class NumberOfTable : ListOfInt() { override fun labelForValue(value: Int, context: Context): String { @@ -507,19 +506,19 @@ sealed class QueryCondition : FilterElementRow { } } - class StartedFromDate : DateQuery() { + class StartedFromDate(date: Date) : DateQuery(date) { override var operator = Operator.MORE } - class StartedToDate : DateQuery() { + class StartedToDate(date: Date) : DateQuery(date) { override var operator = Operator.LESS } - class EndedFromDate : DateQuery() { + class EndedFromDate(date: Date) : DateQuery(date) { override var operator = Operator.MORE } - class EndedToDate : DateQuery() { + class EndedToDate(date: Date) : DateQuery(date) { override var operator = Operator.LESS } @@ -564,7 +563,7 @@ sealed class QueryCondition : FilterElementRow { } } - class PastDay : SingleInt() { + class PastDay(value: Int) : SingleInt(value) { override var operator = Operator.EQUALS override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal @@ -579,9 +578,10 @@ sealed class QueryCondition : FilterElementRow { } } - class Duration : SingleInt() { + class Duration(value: Int) : SingleInt(value) { override var operator = Operator.EQUALS - var minutes: Int? + + var minutes: Int get() { return singleValue } @@ -589,12 +589,9 @@ sealed class QueryCondition : FilterElementRow { singleValue = value } - val netDuration: Long? + val netDuration: Long get() { - minutes?.let { - return (it * 60 * 1000).toLong() - } - return null + return (singleValue * 60 * 1000).toLong() } override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal @@ -609,21 +606,21 @@ sealed class QueryCondition : FilterElementRow { object EndDateNotNull : NotNullQueryCondition() object BigBlindNotNull : NotNullQueryCondition() - class StartedFromTime() : TimeQuery() { + class StartedFromTime(date: Date) : TimeQuery(date) { override var operator = Operator.MORE - constructor(date: Date) : this() { - singleValue = date - } +// constructor(date: Date) : this() { +// singleValue = date +// } } - class EndedToTime() : TimeQuery() { + class EndedToTime(date: Date) : TimeQuery(date) { override var operator = Operator.LESS - constructor(date: Date) : this() { - singleValue = date - } +// constructor(date: Date) : this() { +// singleValue = date +// } } @@ -641,10 +638,6 @@ sealed class QueryCondition : FilterElementRow { class CustomFieldQuery() : QueryDataCondition() { override var entity: Class = CustomField::class.java - - constructor(customField: CustomField) : this() { - this.setObject(customField) - } } open class CustomFieldNumberQuery() : ListOfDouble(), CustomFieldRelated { @@ -712,6 +705,10 @@ sealed class QueryCondition : FilterElementRow { } } + open fun toRowWrapper(filterSectionRow: FilterSectionRow): FilterItemRow { + return BaseFilterItemRow(this, filterSectionRow) + } + /** * main method of the enum * providing a base RealmQuery [realmQuery], the method is able to attached the corresponding query and returns the newly formed [RealmQuery] @@ -762,15 +759,12 @@ sealed class QueryCondition : FilterElementRow { .lessThanOrEqualTo(fieldName, calendar.time.endOfDay()) } is PastDay -> { - singleValue?.let { - val startDate = Date() - val calendar = Calendar.getInstance() - calendar.time = startDate - calendar.add(Calendar.DAY_OF_YEAR, -it) - return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and() - .lessThanOrEqualTo(fieldName, startDate.endOfDay()) - } - return realmQuery + val startDate = Date() + val calendar = Calendar.getInstance() + calendar.time = startDate + calendar.add(Calendar.DAY_OF_YEAR, -singleValue) + return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and() + .lessThanOrEqualTo(fieldName, startDate.endOfDay()) } is DuringThisWeek -> { val calendar = Calendar.getInstance() @@ -800,29 +794,21 @@ sealed class QueryCondition : FilterElementRow { } is StartedFromTime -> { val calendar = Calendar.getInstance() - singleValue?.let { - calendar.time = it - realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) - if (otherQueryCondition is EndedToTime) { - otherQueryCondition.singleValue?.let { endTime -> - calendar.time = endTime - realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) - } - } + calendar.time = singleValue + realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) + if (otherQueryCondition is EndedToTime) { + calendar.time = otherQueryCondition.singleValue + realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) } return realmQuery } is EndedToTime -> { val calendar = Calendar.getInstance() - singleValue?.let { date -> - calendar.time = date - realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) - if (otherQueryCondition is StartedFromTime) { - otherQueryCondition.singleValue?.let { startTime -> - calendar.time = startTime - realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) - } - } + calendar.time = singleValue + realmQuery.lessThanOrEqualTo(fieldName, calendar.hourMinute()) + if (otherQueryCondition is StartedFromTime) { + calendar.time = otherQueryCondition.singleValue + realmQuery.greaterThanOrEqualTo(fieldName, calendar.hourMinute()) } return realmQuery } @@ -846,14 +832,8 @@ sealed class QueryCondition : FilterElementRow { return when (operator) { Operator.EQUALS -> { when (this) { - is SingleDate -> realmQuery.equalTo( - fieldName, - singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) - is SingleInt -> realmQuery.equalTo( - fieldName, - singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) + is SingleDate -> realmQuery.equalTo(fieldName, singleValue) + is SingleInt -> realmQuery.equalTo(fieldName, singleValue) is ListOfInt -> realmQuery.equalTo(fieldName, listOfValues.first()) is ListOfDouble -> realmQuery.equalTo(fieldName, listOfValues.first() * sign) is ListOfString -> realmQuery.equalTo(fieldName, listOfValues.first()) @@ -862,20 +842,11 @@ sealed class QueryCondition : FilterElementRow { } Operator.MORE -> { when (this) { - is SingleDate -> realmQuery.greaterThanOrEqualTo( - fieldName, - singleValue?.startOfDay() ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) - is Duration -> realmQuery.greaterThan( - fieldName, - netDuration ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) + is SingleDate -> realmQuery.greaterThanOrEqualTo(fieldName, singleValue.startOfDay()) + is Duration -> realmQuery.greaterThan(fieldName, netDuration) is TournamentFinalPosition -> realmQuery.greaterThanOrEqualTo(fieldName, listOfValues.first()) is TournamentNumberOfPlayer -> realmQuery.greaterThanOrEqualTo(fieldName, listOfValues.first()) - is SingleInt -> realmQuery.greaterThan( - fieldName, - singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) + is SingleInt -> realmQuery.greaterThan(fieldName, singleValue) is ListOfInt -> realmQuery.greaterThan(fieldName, listOfValues.first()) is NetAmountLost -> realmQuery.lessThan(fieldName, listOfValues.first() * -1) is ListOfDouble -> realmQuery.greaterThan(fieldName, listOfValues.first() * sign) @@ -884,20 +855,11 @@ sealed class QueryCondition : FilterElementRow { } Operator.LESS -> { when (this) { - is SingleDate -> realmQuery.lessThanOrEqualTo( - fieldName, - singleValue?.endOfDay() ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) - is Duration -> realmQuery.lessThan( - fieldName, - netDuration ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) + is SingleDate -> realmQuery.lessThanOrEqualTo(fieldName, singleValue.endOfDay()) + is Duration -> realmQuery.lessThan(fieldName, netDuration) is TournamentFinalPosition -> realmQuery.lessThanOrEqualTo(fieldName, listOfValues.first()) is TournamentNumberOfPlayer -> realmQuery.lessThanOrEqualTo(fieldName, listOfValues.first()) - is SingleInt -> realmQuery.lessThan( - fieldName, - singleValue ?: throw PokerAnalyticsException.FilterElementExpectedValueMissing - ) + is SingleInt -> realmQuery.lessThan(fieldName, singleValue) is ListOfInt -> realmQuery.lessThan(fieldName, listOfValues.first()) is NetAmountLost -> { realmQuery.greaterThan(fieldName, listOfValues.first() * -1) @@ -947,6 +909,7 @@ sealed class QueryCondition : FilterElementRow { } } + override val viewType: Int get() { return when (this) { 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 f23db5bd..5dc1487c 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 @@ -17,6 +17,7 @@ import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterItemRow import timber.log.Timber import java.util.* @@ -89,22 +90,25 @@ open class Filter : RealmObject(), RowRepresentable, Editable, Deletable, Counta return FilterableType.ALL } - fun createOrUpdateFilterConditions(filterConditionRows: ArrayList) { - Timber.d("list of querys saving: ${filterConditionRows.map { it.id }}") + fun createOrUpdateFilterConditions(filterConditionRows: List) { + + Timber.d("list of querys saving: ${filterConditionRows.map { it.queryCondition?.id }}") Timber.d("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}") + filterConditionRows - .map { - it.groupId + .mapNotNull { + it.queryCondition?.groupId } .distinct() .forEach { groupId -> filterConditionRows .filter { - it.groupId == groupId + it.queryCondition?.groupId == groupId } .apply { - Timber.d("list of querys: ${this.map { it.id }}") - val newFilterCondition = FilterCondition(this) + val conditions = this.mapNotNull { it.queryCondition } + Timber.d("list of querys: ${conditions.map { it.id }}") + val newFilterCondition = FilterCondition(conditions, this.first().filterSectionRow) val previousCondition = filterConditions.filter { it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator } @@ -136,17 +140,17 @@ open class Filter : RealmObject(), RowRepresentable, Editable, Deletable, Counta return contained } - /** - * Get the saved value for the given [filterElementRow] - */ - fun loadValueForElement(filterElementRow: QueryCondition) { - val filtered = filterConditions.filter { - it.queryCondition.id == filterElementRow.id - } - if (filtered.isNotEmpty()) { - return filterElementRow.updateValueBy(filtered.first()) - } - } +// /** +// * Get the saved value for the given [filterElementRow] +// */ +// fun loadValueForElement(filterElementRow: QueryCondition) { +// val filtered = filterConditions.filter { +// it.queryCondition.id == filterElementRow.id +// } +// if (filtered.isNotEmpty()) { +// return filterElementRow.updateValueBy(filtered.first()) +// } +// } inline fun query(firstField: String? = null, vararg remainingFields: String): RealmQuery { val realmQuery = realm.where() 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 3a4f1538..7bf65944 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 @@ -4,17 +4,18 @@ import io.realm.RealmList import io.realm.RealmObject import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.model.filter.QueryCondition +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow import java.util.* import kotlin.collections.ArrayList open class FilterCondition() : RealmObject() { - private constructor(filterName:String, sectionName:String) : this() { + private constructor(filterName: String, sectionName: String) : this() { this.filterName = filterName this.sectionName = sectionName } - constructor(filterElementRows: List) : this(filterElementRows.first().baseId, filterElementRows.first().filterSectionRow.name) { + constructor(filterElementRows: List, section: FilterSectionRow) : this(filterElementRows.first().baseId, section.name) { val row = filterElementRows.first() this.filterName ?: throw PokerAnalyticsException.FilterElementUnknownName this.operator = row.operator.ordinal @@ -22,8 +23,8 @@ open class FilterCondition() : RealmObject() { this.stringValue = row.customFieldId } when (row) { - is QueryCondition.SingleInt -> this.setValue(row.singleValue?:throw PokerAnalyticsException.FilterElementExpectedValueMissing) - is QueryCondition.SingleDate -> this.setValue(row.singleValue?:throw PokerAnalyticsException.FilterElementExpectedValueMissing) + is QueryCondition.SingleInt -> this.setValue(row.singleValue) + is QueryCondition.SingleDate -> this.setValue(row.singleValue) is QueryCondition.ListOfDouble -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfDouble).listOfValues }) is QueryCondition.ListOfInt -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfInt).listOfValues }) is QueryCondition.ListOfString -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfString).listOfValues }) @@ -91,4 +92,5 @@ open class FilterCondition() : RealmObject() { fun setValue(value:String) { stringValue = value } + } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt index 5e5794a7..4d6ef051 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt @@ -1,6 +1,5 @@ package net.pokeranalytics.android.ui.modules.filter -import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -10,34 +9,38 @@ import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.fragment_filter_details.* import kotlinx.android.synthetic.main.fragment_filter_details.view.* import net.pokeranalytics.android.R +import net.pokeranalytics.android.exceptions.PAIllegalStateException import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate -import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.fragment.components.RealmFragment 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.FilterElementRow -import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow -import net.pokeranalytics.android.util.NULL_TEXT +import net.pokeranalytics.android.ui.view.rowrepresentable.* import timber.log.Timber import java.util.* -import kotlin.collections.ArrayList -open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { +open class FilterDetailsFragment : RealmFragment(), RowRepresentableDelegate { - val model: FilterViewModel by lazy { + private val activityModel: FilterViewModel by lazy { ViewModelProvider(requireActivity()).get(FilterViewModel::class.java) } + private lateinit var model: FilterDetailsViewModel private lateinit var rowRepresentableAdapter: RowRepresentableAdapter -// private var currentFilter: Filter? = null - private var rows: ArrayList = ArrayList() - private var rowsForFilterSubcategoryRow: HashMap> = HashMap() + companion object { - private val selectedRows = ArrayList() + fun newInstance(categoryRow: FilterCategoryRow): FilterDetailsFragment { + val fragment = FilterDetailsFragment() + val bundle = Bundle() + bundle.putInt(BundleKey.DATA_TYPE.value, categoryRow.ordinal) + fragment.arguments = bundle + return fragment + } + + } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) @@ -57,7 +60,6 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo // requireFragmentManager().popBackStackImmediate() } - /** * Init UI */ @@ -80,39 +82,27 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo */ private fun initData() { -// this.arguments?.let { bundle -> -// -// bundle.getString(BundleKey.PRIMARY_KEY.value)?.let { filterId -> -// this.currentFilter = getRealm().findById(filterId) -// } -// -// val category = bundle.getInt(BundleKey.DATA_TYPE.value) -// this.filterCategoryRow = FilterCategoryRow.values()[category] -// -// } ?: throw PAIllegalStateException("Missing bundle") + this.arguments?.let { bundle -> - //currentFilter = Filter.getFilterBydId(getRealm(), primaryKey) -// currentFilter = FiltersFragment.currentFilter + val filter = this.activityModel.currentFilter ?: throw PAIllegalStateException("Filter is null") + val category = bundle.getInt(BundleKey.DATA_TYPE.value) + val categoryRow = FilterCategoryRow.values()[category] + Timber.d("Category row = $categoryRow") - Timber.d(">> Filter = ${this.model.currentFilter}") - Timber.d("selectedRow = ${this.model.selectedCategoryRow}") + val factory = FilterDetailsViewModelFactory(filter, categoryRow) + this.model = ViewModelProvider(this, factory).get(FilterDetailsViewModel::class.java) - val filterCategoryRow = this.model.filterCategoryRow + } ?: throw PAIllegalStateException("Missing bundle") - this.appBar.toolbar.title = filterCategoryRow.localizedTitle(requireContext()) + //currentFilter = Filter.getFilterBydId(getRealm(), primaryKey) +// currentFilter = FiltersFragment.currentFilter - this.rows.clear() - this.rowsForFilterSubcategoryRow.clear() - this.rows.addAll(filterCategoryRow.filterElements) + Timber.d(">> Filter = ${this.activityModel.currentFilter}") + Timber.d("selectedRow = ${this.activityModel.selectedCategoryRow}") - this.rows.forEach { element -> - if (element is QueryCondition && this.model.currentFilter?.contains(element) == true) { - this.model.currentFilter?.loadValueForElement(element) - this.selectedRows.add(element) - } - } + this.appBar.toolbar.title = this.activityModel.selectedCategoryRow?.localizedTitle(requireContext()) - this.rowRepresentableAdapter = RowRepresentableAdapter(this, this) + this.rowRepresentableAdapter = RowRepresentableAdapter(this.model, this) this.recyclerView.adapter = rowRepresentableAdapter } @@ -125,48 +115,78 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo } when (row) { - is QueryCondition.DateQuery -> DateTimePickerManager.create( - requireContext(), - row, - this, - row.singleValue, - onlyDate = !row.showTime, - onlyTime = row.showTime - ) - is QueryCondition.Duration -> { - var hours: String? = null - var minutes: String? = null - row.minutes?.let { - hours = if (it / 60 > 0) (it / 60).toString() else null - minutes = if (it % 60 > 0) (it % 60).toString() else null + is DateFilterItemRow -> { + when (val condition = row.rawCondition) { + is QueryCondition.DateQuery -> DateTimePickerManager.create( + requireContext(), + row, + this, + row.value, + onlyDate = !condition.showTime, + onlyTime = condition.showTime + ) + } + } + is IntFilterItemRow -> { + when (val condition = row.rawCondition) { + is QueryCondition.Duration -> { + val hours: String? = (condition.minutes / 60).toString() + val minutes: String? = (condition.minutes % 60).toString() + val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes)) + showBottomSheet(row, this, data, true) + } } - val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes)) - showBottomSheet(row, this, data, true) } - is QueryCondition.ListOfValues<*> -> { - var valueAsString: String? = null - row.listOfValues.firstOrNull()?.let { - valueAsString = row.listOfValues.firstOrNull()?.toString() + is ValueListFilterItemRow<*> -> { + when (row.rawCondition) { + is QueryCondition.ListOfValues<*> -> { + var valueAsString: String? = null + row.list.firstOrNull()?.let { + valueAsString = row.list.firstOrNull()?.toString() + } + val data = row.editingDescriptors(mapOf("valueAsString" to valueAsString)) + showBottomSheet(row, this, data, true) + } } - val data = row.editingDescriptors(mapOf("valueAsString" to valueAsString)) - showBottomSheet(row, this, data, true) } } - } - override fun charSequenceForRow(row: RowRepresentable, context: Context): CharSequence { - return when (row) { - is QueryCondition.ListOfValues<*> -> row.firstValue(context) - else -> super.charSequenceForRow(row, context, 0) - } ?: NULL_TEXT - } - override fun isSelected( - position: Int, - row: RowRepresentable, - tag: Int - ): Boolean { - return selectedRows.contains(row) +// (row as? BaseFilterItemRow)?.let { filterItemRow -> +// val rawCondition = filterItemRow.rawCondition +// when (rawCondition) { +// is QueryCondition.DateQuery -> DateTimePickerManager.create( +// requireContext(), +// row, +// this, +// filterItemRow.value, +// onlyDate = !row.showTime, +// onlyTime = row.showTime +// ) +// is QueryCondition.Duration -> { +// val hours: String? = (row.minutes / 60).toString() +// val minutes: String? = (row.minutes % 60).toString() +//// row.minutes?.let { +//// hours = if (it / 60 > 0) (it / 60).toString() else null +//// minutes = if (it % 60 > 0) (it % 60).toString() else null +//// } +// val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes)) +// showBottomSheet(row, this, data, true) +// } +// is QueryCondition.ListOfValues<*> -> { +// var valueAsString: String? = null +// row.listOfValues.firstOrNull()?.let { +// valueAsString = row.listOfValues.firstOrNull()?.toString() +// } +// val data = row.editingDescriptors(mapOf("valueAsString" to valueAsString)) +// showBottomSheet(row, this, data, true) +// } +// else -> { } +// } +// +// } + + } override fun onRowValueChanged(value: Any?, row: RowRepresentable) { @@ -174,80 +194,89 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo Timber.d("onRowValueChanged: $row $value") when (row) { - is QueryCondition.DateQuery -> row.singleValue = if (value != null && value is Date) value else null - is QueryCondition.Duration -> { - if (value is ArrayList<*>) { - val hours: Int? = try { - (value[0] as String?)?.toInt() - } catch (e: Exception) { - null - } - val minutes = try { - (value[1] as String?)?.toInt() - } catch (e: Exception) { - null + is DateFilterItemRow -> { + row.value = value as Date + } + is IntFilterItemRow -> { + when (value) { + is Int -> { + row.value = value } - if (hours != null && minutes != null) { - row.minutes = hours * 60 + minutes - } else if (hours != null) { - row.minutes = hours * 60 - } else if (minutes != null) { - row.minutes = minutes + is ArrayList<*> -> { + val hours: Int? = try { + (value[0] as String?)?.toInt() + } catch (e: Exception) { + null + } + val minutes = try { + (value[1] as String?)?.toInt() + } catch (e: Exception) { + null + } + if (hours != null && minutes != null) { + row.value = hours * 60 + minutes + } else if (hours != null) { + row.value = hours * 60 + } else if (minutes != null) { + row.value = minutes + } } - } else { - row.minutes = null } } - is QueryCondition.SingleInt -> row.singleValue = if (value != null && value is String) value.toInt() else null - is QueryCondition.ListOfDouble -> row.listOfValues = arrayListOf().apply { - if (value != null && value is String) this.add(value.toDouble()) + is DoubleValueListFilterItemRow -> { + val string = value as String + row.add(string.toDouble()) } - is QueryCondition.ListOfInt -> row.listOfValues = arrayListOf().apply { - if (value != null && value is String) this.add(value.toInt()) + is IntValueListFilterItemRow -> { + val string = value as String + row.add(string.toInt()) } - is QueryCondition.ListOfString -> row.listOfValues = arrayListOf().apply { - if (value != null && value is String) this.add(value) + is StringValueListFilterItemRow -> { + row.add(value as String) } } +// when (row) { +// is QueryCondition.DateQuery -> { +// if (value != null && value is Date) { +// row.singleValue = value +// } +// } +// is QueryCondition.Duration -> { +// if (value is ArrayList<*>) { +// +// } +// } +// is QueryCondition.SingleInt -> { +// if (value != null && value is String) { +// row.singleValue = value.toInt() +// } +// } +// is QueryCondition.ListOfDouble -> row.listOfValues = arrayListOf().apply { +// if (value != null && value is String) this.add(value.toDouble()) +// } +// is QueryCondition.ListOfInt -> row.listOfValues = arrayListOf().apply { +// if (value != null && value is String) this.add(value.toInt()) +// } +// is QueryCondition.ListOfString -> row.listOfValues = arrayListOf().apply { +// if (value != null && value is String) this.add(value) +// } +// } + // Remove the row before updating the selected rows list - selectedRows.remove(row as FilterElementRow) + this.model.selectedRows.remove(row as FilterItemRow) updateRowsSelection(row, value == null) } - override fun adapterRows(): List? { - return rows - } - - override fun viewTypeForPosition(position: Int): Int { - val rowViewType = rowRepresentableForPosition(position)?.viewType ?: -1 - return if (rowViewType != -1) rowViewType else RowViewType.TITLE_CHECK.ordinal - } - /** * Update rows selection */ private fun updateRowsSelection(row: RowRepresentable, forceDeselection: Boolean = false) { - if (selectedRows.contains(row) || forceDeselection) { - selectedRows.remove(row) - } else { - if (row is FilterElementRow) { - row.sectionToExclude?.let { filterSectionToExclude -> - val excludedFilters = selectedRows.filter { - filterSectionToExclude.contains(it.filterSectionRow) - } - excludedFilters.forEach { - selectedRows.remove(it) - rowRepresentableAdapter.refreshRow(it) - } - } - selectedRows.add(row as QueryCondition) - } - } + this.model.updateRowsSelection(this.rowRepresentableAdapter, row, forceDeselection) - println("list of selected rows : $selectedRows") +// println("list of selected rows : $selectedRows") // Update UI this.rowRepresentableAdapter.refreshRow(row) @@ -258,18 +287,23 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo */ private fun saveData() { - val currentFilter = this.model.currentFilter + val currentFilter = this.activityModel.currentFilter //TODO: Save currentFilter details data Timber.d("Save data for queryWith: ${currentFilter?.id}") - this.selectedRows.forEach { + this.model.selectedRows.forEach { Timber.d("Selected rows: $it") } - getRealm().executeTransaction { - currentFilter?.remove(this.model.filterCategoryRow) - currentFilter?.createOrUpdateFilterConditions(this.selectedRows) - } + this.activityModel.selectedCategoryRow?.let { category -> + + getRealm().executeTransaction { + currentFilter?.remove(category) + val validConditions = this.model.selectedRows.filter { it.queryCondition != null } + currentFilter?.createOrUpdateFilterConditions(validConditions) + } + } + currentFilter?.filterConditions?.forEach { Timber.d("Condition: $it") } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsViewModel.kt new file mode 100644 index 00000000..ba9885b0 --- /dev/null +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsViewModel.kt @@ -0,0 +1,116 @@ +package net.pokeranalytics.android.ui.modules.filter + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import net.pokeranalytics.android.model.filter.QueryCondition +import net.pokeranalytics.android.model.realm.Filter +import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter +import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource +import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.RowViewType +import net.pokeranalytics.android.ui.view.rowrepresentable.BaseFilterItemRow +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterItemRow +import net.pokeranalytics.android.ui.view.rowrepresentable.ValueListFilterItemRow +import net.pokeranalytics.android.util.NULL_TEXT +import timber.log.Timber + +class FilterDetailsViewModelFactory(var filter: Filter, var categoryRow: FilterCategoryRow): ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return FilterDetailsViewModel(categoryRow, filter) as T + } + +} + +class FilterDetailsViewModel(categoryRow: FilterCategoryRow, var filter: Filter) : ViewModel(), StaticRowRepresentableDataSource { + + private var rows: ArrayList = ArrayList() + val selectedRows = ArrayList() + +// private var rowsForFilterSubcategoryRow: HashMap> = +// HashMap() + + init { + this.rows.addAll(categoryRow.filterElements) + this.defineSelectedItems() + } + + override fun adapterRows(): List? { + return this.rows + } + + override fun charSequenceForRow(row: RowRepresentable, context: Context): CharSequence { + + val filterItemRow = row as? BaseFilterItemRow + filterItemRow?.let { + return when (val condition = it.rawCondition) { + is QueryCondition.ListOfValues<*> -> condition.firstValue(context) ?: NULL_TEXT + else -> super.charSequenceForRow(row, context, 0) + } + } + return NULL_TEXT + +// return when (row) { +// is BaseFilterItemRow -> { +// +// } +// is QueryCondition.ListOfValues<*> -> row.firstValue(context) +// else -> super.charSequenceForRow(row, context, 0) +// } ?: NULL_TEXT + } + + override fun isSelected(position: Int, row: RowRepresentable, tag: Int): Boolean { + return this.selectedRows.contains(row) + } + + override fun viewTypeForPosition(position: Int): Int { + val rowRepresentable = rowRepresentableForPosition(position) + val rowViewType = rowRepresentable?.viewType ?: -1 +// Timber.d("found viewtype = $rowViewType, rr = $rowRepresentable") + return if (rowViewType != -1) rowViewType else RowViewType.TITLE_CHECK.ordinal + } + + private fun defineSelectedItems() { + this.rows.filterIsInstance().forEach { item -> + + val condition = item.queryCondition + if (condition != null && this.filter.contains(condition)) { + + // Load items with appropriate value + this.filter.filterConditions.firstOrNull { + it.queryCondition.id == condition.id + }?.let { + item.updateValue(it) +// item.queryCondition?.updateValueBy(it) + this.selectedRows.add(item) + } + } + + } + + } + + fun updateRowsSelection(adapter: RowRepresentableAdapter, row: RowRepresentable, forceDeselection: Boolean = false) { + + if (this.selectedRows.contains(row) || forceDeselection) { + this.selectedRows.remove(row) + } else { + if (row is FilterItemRow) { + row.sectionToExclude?.let { filterSectionToExclude -> + val excludedFilters = this.selectedRows.filter { + filterSectionToExclude.contains(it.filterSectionRow) + } + excludedFilters.forEach { + this.selectedRows.remove(it) + adapter.refreshRow(it) + } + } + this.selectedRows.add(row) + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterViewModel.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterViewModel.kt index 450c633a..b3e4661d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterViewModel.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterViewModel.kt @@ -20,14 +20,16 @@ class FilterViewModel : ViewModel(), StaticRowRepresentableDataSource { var filterCopy: Filter? = null private var categoryRows: ArrayList = ArrayList() var primaryKey: String? = null - var selectedCategoryRow: RowRepresentable? = null + + var selectedCategoryRow: FilterCategoryRow? = null + var isUpdating = false - // Details - val filterCategoryRow: FilterCategoryRow - get() { - return this.selectedCategoryRow as FilterCategoryRow - } + +// val filterCategoryRow: FilterCategoryRow +// get() { +// return this.selectedCategoryRow as FilterCategoryRow +// } fun init(realm: Realm) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt index 8bb9d5a8..b4ff29bd 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt @@ -6,8 +6,10 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R +import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.ui.activity.components.BaseActivity import net.pokeranalytics.android.ui.fragment.components.BaseFragment +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow class FiltersActivity : BaseActivity() { @@ -102,9 +104,9 @@ class FiltersActivity : BaseActivity() { } - fun showDetailsFragment() { + fun showDetailsFragment(categoryRow: FilterCategoryRow) { - val detailsFragment = FilterDetailsFragment() + val detailsFragment = FilterDetailsFragment.newInstance(categoryRow) val fragmentTransaction = this.supportFragmentManager.beginTransaction() fragmentTransaction.replace(R.id.container, detailsFragment) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt index 88b5894a..57e610b3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt @@ -21,6 +21,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.view.RowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.extensions.sorted import timber.log.Timber @@ -148,10 +149,11 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate { override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) { super.onRowSelected(position, row, tag) - this.model.selectedCategoryRow = row - this.model.currentFilter?.id?.let { _ -> + val categoryRow = row as FilterCategoryRow + this.model.selectedCategoryRow = categoryRow + this.model.currentFilter?.let { _ -> - (activity as FiltersActivity).showDetailsFragment() + (activity as FiltersActivity).showDetailsFragment(categoryRow) // this.model.filterCategoryRow = row as FilterCategoryRow diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterCategoryRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterCategoryRow.kt index 362001bc..362c09a5 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterCategoryRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterCategoryRow.kt @@ -63,7 +63,13 @@ enum class FilterCategoryRow(override val resId: Int?, override val viewType: In val filterElements: List get() { return filterSectionRows.flatMap { - it.filterElements + val items = it.filterItems + val list = mutableListOf() + if (items.isNotEmpty()) { + list.add(it) + list.addAll(it.filterItems) + } + list } } 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 43860b3a..dc6cc88d 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,22 @@ package net.pokeranalytics.android.ui.view.rowrepresentable +import android.content.Context import android.text.InputType import net.pokeranalytics.android.R import net.pokeranalytics.android.model.filter.QueryCondition +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.util.* -interface FilterElementRow : RowRepresentable { +interface FilterItemRow : RowRepresentable { + + val queryCondition: QueryCondition? + var filterSectionRow: FilterSectionRow + override fun editingDescriptors(map: Map): ArrayList? { - return when (this) { + return when (this.queryCondition) { is QueryCondition.Duration -> { val hours: String? by map val minutes: String? by map @@ -22,9 +27,9 @@ interface FilterElementRow : RowRepresentable { } is QueryCondition.ListOfValues<*> -> { val valueAsString: String? by map - val hint = when (this.operator) { + val hint = when (this.queryCondition?.operator) { QueryCondition.Operator.MORE, QueryCondition.Operator.LESS -> { - when (this) { + when (this.queryCondition) { is QueryCondition.CustomFieldNumberQuery -> R.string.value is QueryCondition.CustomFieldAmountQuery -> R.string.amount else -> this.filterSectionRow.resId @@ -41,7 +46,7 @@ interface FilterElementRow : RowRepresentable { } } - var filterSectionRow: FilterSectionRow + fun updateValue(it: FilterCondition) { } val sectionToExclude: List? get() { @@ -58,4 +63,97 @@ interface FilterElementRow : RowRepresentable { } return null } -} \ No newline at end of file +} + +open class BaseFilterItemRow(queryCondition: QueryCondition, override var filterSectionRow: FilterSectionRow) : FilterItemRow { + + var rawCondition: QueryCondition = queryCondition + private set + + override val queryCondition: QueryCondition? + get() { return rawCondition } + + // Row Representable + + override fun getDisplayName(context: Context): String { + return this.rawCondition.getDisplayName(context) + } + + override val viewType: Int + get() { return this.rawCondition.viewType } + + override val bottomSheetType: BottomSheetType + get() { return this.rawCondition.bottomSheetType } + +} + + +abstract class ValueFilterItemRow>(queryCondition: QueryCondition.SingleValue, filterSectionRow: FilterSectionRow): BaseFilterItemRow(queryCondition, filterSectionRow) { + + private var valueCondition: QueryCondition.SingleValue = queryCondition + + var value: T? = null + + override val queryCondition: QueryCondition? + get() { + value?.let { + valueCondition.singleValue = it + return valueCondition + } + return null + } + + override fun updateValue(fc: FilterCondition) { + + // TODO + +// this.value = fc.queryCondition. + + } + +} + +abstract class ValueListFilterItemRow>(queryCondition: QueryCondition.ListOfValues, filterSectionRow: FilterSectionRow): BaseFilterItemRow(queryCondition, filterSectionRow) { + + private var listCondition: QueryCondition.ListOfValues = queryCondition + + var list: MutableList = mutableListOf() + private set + + fun add(value: T) { + this.list.add(value) + } + + override val queryCondition: QueryCondition? + get() { + return if (list.isNotEmpty()) { + listCondition.listOfValues = list + listCondition + } else { + null + } + } + +// override fun getDisplayName(context: Context): String { +// return this.listCondition.getDisplayName(context, this.list) +// } + + override fun updateValue(fc: FilterCondition) { + + // TODO + +// this.value = fc.queryCondition. + + } +} + +class DateFilterItemRow(queryCondition: QueryCondition.SingleDate, filterSectionRow: FilterSectionRow): ValueFilterItemRow(queryCondition, filterSectionRow) +class IntFilterItemRow(queryCondition: QueryCondition.SingleInt, filterSectionRow: FilterSectionRow): ValueFilterItemRow(queryCondition, filterSectionRow) + +class IntValueListFilterItemRow(queryCondition: QueryCondition.ListOfInt, filterSectionRow: FilterSectionRow): ValueListFilterItemRow(queryCondition, filterSectionRow) +class DoubleValueListFilterItemRow(queryCondition: QueryCondition.ListOfDouble, filterSectionRow: FilterSectionRow): ValueListFilterItemRow(queryCondition, filterSectionRow) +class StringValueListFilterItemRow(queryCondition: QueryCondition.ListOfString, filterSectionRow: FilterSectionRow): ValueListFilterItemRow(queryCondition, filterSectionRow) + +//interface FilterElementRow : RowRepresentable { +// +//} \ No newline at end of file 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 319eee8d..b058aeaf 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.filter.QueryCondition import net.pokeranalytics.android.model.filter.mapFirstCondition import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType +import java.util.* sealed class FilterSectionRow(override val resId: Int?) : RowRepresentable { object CashOrTournament: FilterSectionRow(R.string.cash_or_tournament) @@ -69,101 +70,118 @@ sealed class FilterSectionRow(override val resId: Int?) : RowRepresentable { val allowMultiSelection: Boolean get() = (this.selectionType == SelectionType.MULTIPLE) - val filterElements: List + val filterItems: List get() { + return this.queryConditions.map { + it.toRowWrapper(this@FilterSectionRow) + } + } + + private val queryConditions: List + get() { + return when (this@FilterSectionRow) { + // General + CashOrTournament -> Criteria.SessionTypes.queryConditions.mapFirstCondition() + LiveOrOnline -> Criteria.BankrollTypes.queryConditions.mapFirstCondition() + Game -> Criteria.Games.queryConditions.mapFirstCondition() + LimitType -> Criteria.Limits.queryConditions.mapFirstCondition() + TableSize -> Criteria.TableSizes.queryConditions.mapFirstCondition() + // Date + DynamicDate -> arrayListOf( + QueryCondition.IsToday, + QueryCondition.WasYesterday, + QueryCondition.WasTodayAndYesterday, + QueryCondition.DuringThisWeek, + QueryCondition.DuringThisMonth, + QueryCondition.DuringThisYear + ) + WeekdayOrWeekend -> arrayListOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd) + Year -> Criteria.Years.queryConditions.mapFirstCondition() + DayOfWeek -> Criteria.DaysOfWeek.queryConditions.mapFirstCondition() + MonthOfYear -> Criteria.MonthsOfYear.queryConditions.mapFirstCondition() + + // Duration + SessionDuration -> QueryCondition.moreOrLess() + + FixedDate -> arrayListOf(QueryCondition.StartedFromDate(Date()), QueryCondition.EndedToDate(Date())) // dummy constructor values + Duration -> arrayListOf(QueryCondition.PastDay(0)) // dummy constructor values + TimeFrameRange -> arrayListOf(QueryCondition.StartedFromTime(Date()), QueryCondition.EndedToTime(Date())) // dummy constructor values - val data = arrayListOf().apply { - this.addAll( - when (this@FilterSectionRow) { - - // General - CashOrTournament -> Criteria.SessionTypes.queryConditions.mapFirstCondition() - LiveOrOnline -> Criteria.BankrollTypes.queryConditions.mapFirstCondition() - Game -> Criteria.Games.queryConditions.mapFirstCondition() - LimitType -> Criteria.Limits.queryConditions.mapFirstCondition() - TableSize -> Criteria.TableSizes.queryConditions.mapFirstCondition() - // Date - DynamicDate -> arrayListOf( - QueryCondition.IsToday, - QueryCondition.WasYesterday, - QueryCondition.WasTodayAndYesterday, - QueryCondition.DuringThisWeek, - QueryCondition.DuringThisMonth, - QueryCondition.DuringThisYear - ) - FixedDate -> arrayListOf(QueryCondition.StartedFromDate(), QueryCondition.EndedToDate()) - Duration -> arrayListOf(QueryCondition.PastDay()) - WeekdayOrWeekend -> arrayListOf(QueryCondition.IsWeekDay, QueryCondition.IsWeekEnd) - Year -> Criteria.Years.queryConditions.mapFirstCondition() - DayOfWeek -> Criteria.DaysOfWeek.queryConditions.mapFirstCondition() - MonthOfYear -> Criteria.MonthsOfYear.queryConditions.mapFirstCondition() - - // Duration - SessionDuration -> QueryCondition.moreOrLess() - TimeFrameRange -> arrayListOf(QueryCondition.StartedFromTime(), QueryCondition.EndedToTime()) - - // Sessions - //Sessions -> arrayListOf(QueryCondition.LastGame(), QueryCondition.LastSession()) - - // Cash - Blind -> Criteria.Blinds.queryConditions.mapFirstCondition() + // Sessions + //Sessions -> arrayListOf(QueryCondition.LastGame(), QueryCondition.LastSession()) + + // Cash + Blind -> Criteria.Blinds.queryConditions.mapFirstCondition() // CashRebuyCount -> QueryCondition.moreOrLess() - // Tournament - TournamentType -> Criteria.TournamentTypes.queryConditions.mapFirstCondition() + // Tournament + TournamentType -> Criteria.TournamentTypes.queryConditions.mapFirstCondition() // CompletionPercentage -> arrayListOf() - TournamentFinalPosition -> QueryCondition.moreOrLess() - TournamentNumberOfPlayer -> QueryCondition.moreOrLess() - TournamentEntryFee -> Criteria.TournamentFees.queryConditions.mapFirstCondition() - TournamentName -> Criteria.TournamentNames.queryConditions.mapFirstCondition() - TournamentFeature -> Criteria.TournamentFeatures.queryConditions.mapFirstCondition() - Location -> Criteria.Locations.queryConditions.mapFirstCondition() - Bankroll -> Criteria.Bankrolls.queryConditions.mapFirstCondition() - MultiTabling -> QueryCondition.moreOrLess() - //NumberOfPlayers -> QueryCondition.moreOrLess() - NumberOfRebuy -> QueryCondition.moreOrLess() + TournamentFinalPosition -> QueryCondition.moreOrLess() + TournamentNumberOfPlayer -> QueryCondition.moreOrLess() + TournamentEntryFee -> Criteria.TournamentFees.queryConditions.mapFirstCondition() + TournamentName -> Criteria.TournamentNames.queryConditions.mapFirstCondition() + TournamentFeature -> Criteria.TournamentFeatures.queryConditions.mapFirstCondition() + Location -> Criteria.Locations.queryConditions.mapFirstCondition() + Bankroll -> Criteria.Bankrolls.queryConditions.mapFirstCondition() + MultiTabling -> QueryCondition.moreOrLess() + //NumberOfPlayers -> QueryCondition.moreOrLess() + NumberOfRebuy -> QueryCondition.moreOrLess() // MultiPlayer -> arrayListOf() - Value -> arrayListOf().apply { - addAll(QueryCondition.moreOrLess()) - addAll(QueryCondition.moreOrLess()) - } - TransactionType -> Criteria.TransactionTypes.queryConditions.mapFirstCondition() - is CustomField -> { - val cf = this@FilterSectionRow.customField - if (cf.isListType) { - Criteria.ListCustomFields(cf.id).queryConditions.mapFirstCondition() - } else if (cf.isAmountType) { - QueryCondition.moreOrLess().apply { - this.forEach { - it.customFieldId = cf.id - } - } - } else { - QueryCondition.moreOrLess().apply { - this.forEach { - it.customFieldId = cf.id - } - } - } - } - else -> arrayListOf() - }.apply { - this.forEach { - it.filterSectionRow = this@FilterSectionRow + Value -> arrayListOf().apply { + addAll(QueryCondition.moreOrLess()) + addAll(QueryCondition.moreOrLess()) + } + TransactionType -> Criteria.TransactionTypes.queryConditions.mapFirstCondition() + is CustomField -> { + val cf = this@FilterSectionRow.customField + when { + cf.isListType -> { + Criteria.ListCustomFields(cf.id).queryConditions.mapFirstCondition() + } + cf.isAmountType -> { + QueryCondition.moreOrLess().apply { + this.forEach { + it.customFieldId = cf.id + } + } + } + else -> { + QueryCondition.moreOrLess().apply { + this.forEach { + it.customFieldId = cf.id + } + } } } - ) + } + else -> arrayListOf() } + } - // Add the section row only if we have data for this section - if (data.isNotEmpty()) { - data.add(0, this@FilterSectionRow) - } - return data - } +// val filterElements: List +// get() { +// +// val data = arrayListOf().apply { +// this.addAll( +// filterItems.apply { +// this.forEach { +// it.filterSectionRow = this@FilterSectionRow +// } +// } +// ) +// } +// +// // Add the section row only if we have data for this section +// if (data.isNotEmpty()) { +// data.add(0, this@FilterSectionRow) +// } +// +// return data +// } private val selectionType: SelectionType get() {