Fixes compilation issue + use latest refactored methods

dev
Laurent 7 years ago
commit 4fbc01af05
  1. 9
      app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt
  2. 149
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt
  3. 5
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  4. 15
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  5. 145
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  6. 84
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  7. 14
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  8. 169
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  9. 6
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  10. 4
      app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableAdapter.kt
  11. 24
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  12. 35
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt
  13. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt
  14. 58
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt
  15. 14
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  16. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/ScreenSlidePageFragment.kt
  17. 13
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetFragment.kt
  18. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetStaticListFragment.kt
  19. 138
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  20. 12
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  21. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
  22. 6
      app/src/main/java/net/pokeranalytics/android/ui/view/RowRepresentable.kt
  23. 17
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  24. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterCategoryRow.kt
  25. 6
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/FilterSectionRow.kt
  26. 4
      app/src/main/java/net/pokeranalytics/android/util/extensions/NumbersExtension.kt
  27. 0
      app/src/main/res/drawable/ic_baseline_all_inclusive.xml
  28. 0
      app/src/main/res/drawable/ic_baseline_email.xml
  29. 0
      app/src/main/res/drawable/ic_baseline_vpn_key.xml
  30. 0
      app/src/main/res/drawable/ic_baseline_wifi_off.xml
  31. 9
      app/src/main/res/drawable/ic_list.xml
  32. 31
      app/src/main/res/layout/fragment_custom_view.xml
  33. 87
      app/src/main/res/layout/fragment_screen_slide_page.xml
  34. 24
      app/src/main/res/layout/fragment_subscription.xml
  35. 3
      app/src/main/res/layout/row_title_value_action.xml
  36. 10
      app/src/main/res/menu/toolbar_bottom_sheet.xml

@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.filter
import android.content.Context import android.content.Context
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
fun List<Query>.mapFirstCondition() : List<QueryCondition> { fun List<Query>.mapFirstCondition() : List<QueryCondition> {
@ -67,7 +68,13 @@ class Query {
} }
} }
return realmQuery val queryLast = this.conditions.filter {
it is QueryCondition.Last
}.firstOrNull()
queryLast?.let {
return realmQuery.limit((it as QueryCondition.Last).singleValue.toLong())
}
return realmQuery
} }
fun merge(query: Query) : Query { fun merge(query: Query) : Query {

@ -20,10 +20,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.UserDefaults import net.pokeranalytics.android.util.UserDefaults
import net.pokeranalytics.android.util.extensions.endOfDay import net.pokeranalytics.android.util.extensions.*
import net.pokeranalytics.android.util.extensions.hourMinute
import net.pokeranalytics.android.util.extensions.startOfDay
import net.pokeranalytics.android.util.extensions.toCurrency
import java.text.DateFormatSymbols import java.text.DateFormatSymbols
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -80,9 +77,7 @@ sealed class QueryCondition : FilterElementRow {
MORE, MORE,
LESS, LESS,
EQUALS, EQUALS,
BETWEEN, TRUE,
BETWEEN_RIGHT_EXCLUSIVE,
BETWEEN_LEFT_EXCLUSIVE,
; ;
} }
@ -111,7 +106,7 @@ sealed class QueryCondition : FilterElementRow {
} }
} }
open var operator: Operator = Operator.ANY abstract var operator: Operator
abstract class ListOfValues<T>: QueryCondition(), Comparable<ListOfValues<T>> where T:Comparable<T> { abstract class ListOfValues<T>: QueryCondition(), Comparable<ListOfValues<T>> where T:Comparable<T> {
@ -138,29 +133,37 @@ sealed class QueryCondition : FilterElementRow {
abstract class ListOfDouble: ListOfValues<Double>() { abstract class ListOfDouble: ListOfValues<Double>() {
open var sign: Int = 1 open var sign: Int = 1
override var operator: Operator = Operator.ANY
override var listOfValues = arrayListOf(0.0) override var listOfValues = arrayListOf(0.0)
override fun updateValueBy(filterCondition: FilterCondition) { override fun updateValueBy(filterCondition: FilterCondition) {
super.updateValueBy(filterCondition) super.updateValueBy(filterCondition)
listOfValues = filterCondition.getValues() listOfValues = filterCondition.getValues()
} }
override fun labelForValue(value: Double, context: Context): String { override fun labelForValue(value: Double, context: Context): String {
return value.toCurrency(UserDefaults.currency) val prefix = this.resId?.let {
context.getString(it)+" "
} ?: ""
return prefix+value.toCurrency(UserDefaults.currency)
} }
} }
abstract class ListOfInt: ListOfValues<Int>() { abstract class ListOfInt: ListOfValues<Int>() {
override var operator: Operator = Operator.ANY
override var listOfValues = arrayListOf(0) override var listOfValues = arrayListOf(0)
override fun updateValueBy(filterCondition: FilterCondition) { override fun updateValueBy(filterCondition: FilterCondition) {
super.updateValueBy(filterCondition) super.updateValueBy(filterCondition)
listOfValues = filterCondition.getValues() listOfValues = filterCondition.getValues()
} }
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return value.toString() val prefix = this.resId?.let {
context.getString(it)+" "
} ?: ""
return prefix+value.toString()
} }
} }
abstract class ListOfString: ListOfValues<String>() { abstract class ListOfString: ListOfValues<String>() {
override var operator: Operator = Operator.ANY
override var listOfValues = ArrayList<String>() override var listOfValues = ArrayList<String>()
override fun labelForValue(value: String, context: Context): String { return value } override fun labelForValue(value: String, context: Context): String { return value }
override fun updateValueBy(filterCondition: FilterCondition) { override fun updateValueBy(filterCondition: FilterCondition) {
@ -171,8 +174,12 @@ sealed class QueryCondition : FilterElementRow {
abstract class SingleDate: SingleValue<Date>() { abstract class SingleDate: SingleValue<Date>() {
override fun labelForValue(value: Date, context: Context): String { override fun labelForValue(value: Date, context: Context): String {
return value.toString() val prefix = this.resId?.let {
context.getString(it)+" "
} ?: ""
return prefix+value.shortDate()
} }
override var listOfValues = ArrayList<Date>() override var listOfValues = ArrayList<Date>()
override var singleValue: Date override var singleValue: Date
@ -190,8 +197,12 @@ sealed class QueryCondition : FilterElementRow {
abstract class SingleInt: SingleValue<Int>() { abstract class SingleInt: SingleValue<Int>() {
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return value.toString() val prefix = this.resId?.let {
context.getString(it)+" "
} ?: ""
return prefix+value.toString()
} }
override var singleValue: Int override var singleValue: Int
get() { return listOfValues.firstOrNull() ?: 0 } get() { return listOfValues.firstOrNull() ?: 0 }
set(value) { set(value) {
@ -205,7 +216,12 @@ sealed class QueryCondition : FilterElementRow {
} }
} }
override fun getDisplayName(context: Context): String { return baseId } override fun getDisplayName(context: Context): String {
this.resId?.let {
return context.getString(it)
}
return baseId
}
override var filterSectionRow: FilterSectionRow = FilterSectionRow.CASH_TOURNAMENT override var filterSectionRow: FilterSectionRow = FilterSectionRow.CASH_TOURNAMENT
@ -250,21 +266,17 @@ sealed class QueryCondition : FilterElementRow {
override val showTime: Boolean = true override val showTime: Boolean = true
} }
object IsLive : QueryCondition() { abstract class TrueQueryCondition: QueryCondition() {
override fun getDisplayName(context: Context): String { return "Live" } override var operator: Operator = Operator.TRUE
} }
object IsCash : QueryCondition() { object IsLive : TrueQueryCondition()
override fun getDisplayName(context: Context): String { return "Cash" }
}
object IsOnline : QueryCondition() { object IsCash : TrueQueryCondition()
override fun getDisplayName(context: Context): String { return "Online" }
}
object IsTournament : QueryCondition() { object IsOnline : TrueQueryCondition()
override fun getDisplayName(context: Context): String { return "Tournament" }
} object IsTournament : TrueQueryCondition()
class AnyBankroll(): QueryDataCondition<Bankroll>() { class AnyBankroll(): QueryDataCondition<Bankroll>() {
override var entity: Class<Bankroll> = Bankroll::class.java override var entity: Class<Bankroll> = Bankroll::class.java
@ -336,30 +348,64 @@ sealed class QueryCondition : FilterElementRow {
class AnyBlind: ListOfString() class AnyBlind: ListOfString()
class LastGame: SingleInt() object Last: SingleInt() {
class LastSession: 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() class NumberOfTable: ListOfInt() {
override fun labelForValue(value: Int, context: Context): String {
val prefix = this.resId?.let {
context.getString(it) + " "
} ?: ""
return prefix + value.toString() + " " + context.getString(R.string.tables)
}
}
class NumberOfRebuy(): ListOfDouble() { class NumberOfRebuy(): ListOfDouble() {
constructor(operator: Operator, numberOfRebuy: Double) : this() { constructor(operator: Operator, numberOfRebuy: Double) : this() {
this.operator = operator this.operator = operator
this.listOfValues = arrayListOf(numberOfRebuy) this.listOfValues = arrayListOf(numberOfRebuy)
} }
override fun labelForValue(value: Double, context: Context): String {
val prefix = this.resId?.let {
context.getString(it) + " "
} ?: ""
return prefix + value.toString()
}
} }
open class TournamentFinalPosition(): ListOfInt() { class TournamentFinalPosition(): ListOfInt() {
constructor(operator: Operator, finalPosition: Int) : this() { constructor(operator: Operator, finalPosition: Int) : this() {
this.operator = operator this.operator = operator
this.listOfValues = arrayListOf(finalPosition) this.listOfValues = arrayListOf(finalPosition)
} }
override fun labelForValue(value: Int, context: Context): String {
val prefix = this.resId?.let {
context.getString(it) + " "
} ?: ""
return prefix + value.toString()
}
} }
open class NetAmount: ListOfDouble() open class NetAmount: ListOfDouble()
class NetAmountWon: NetAmount() class NetAmountWon: NetAmount()
class NetAmountLost: NetAmount() { override var sign: Int = -1 } class NetAmountLost: NetAmount() { override var sign: Int = -1 }
class TournamentNumberOfPlayer: ListOfInt() class TournamentNumberOfPlayer: ListOfInt() {
override fun labelForValue(value: Int, context: Context): String {
val prefix = this.resId?.let {
context.getString(it) + " "
} ?: ""
return prefix + value.toString() + context.getString(R.string.number_of_players)
}
}
class StartedFromDate: DateQuery() { override var operator = Operator.MORE } class StartedFromDate: DateQuery() { override var operator = Operator.MORE }
class StartedToDate: DateQuery() { override var operator = Operator.LESS } class StartedToDate: DateQuery() { override var operator = Operator.LESS }
@ -392,26 +438,38 @@ sealed class QueryCondition : FilterElementRow {
} }
} }
object IsWeekDay: QueryCondition() object IsWeekDay: TrueQueryCondition()
object IsWeekEnd: QueryCondition() object IsWeekEnd: TrueQueryCondition()
object IsToday: QueryCondition() object IsToday: TrueQueryCondition()
object WasYesterday: QueryCondition() object WasYesterday: TrueQueryCondition()
object WasTodayAndYesterday: QueryCondition() object WasTodayAndYesterday: TrueQueryCondition()
object DuringThisWeek: QueryCondition() object DuringThisWeek: TrueQueryCondition()
object DuringThisMonth: QueryCondition() object DuringThisMonth: TrueQueryCondition()
object DuringThisYear: QueryCondition() object DuringThisYear: TrueQueryCondition()
class TournamentFee: ListOfDouble() { class TournamentFee: ListOfDouble() {
override fun labelForValue(value: Double, context: Context): String { override fun labelForValue(value: Double, context: Context): String {
return value.toCurrency(UserDefaults.currency) val prefix = this.resId?.let {
context.getString(it)+" "
} ?: ""
return prefix+value.toCurrency(UserDefaults.currency)
} }
} }
class PastDay: SingleInt() { class PastDay: SingleInt() {
override var operator = Operator.EQUALS
override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal
override fun labelForValue(value: Int, context: Context): String {
val suffix = this.resId?.let {
" "+context.getString(it)
} ?: ""
return value.toString() + suffix
}
} }
class Duration: SingleInt() { class Duration: SingleInt() {
override var operator = Operator.EQUALS
var minutes:Int var minutes:Int
get() { return singleValue } get() { return singleValue }
set(value) { listOfValues = arrayListOf(value) } set(value) { listOfValues = arrayListOf(value) }
@ -473,6 +531,13 @@ sealed class QueryCondition : FilterElementRow {
calendar.add(Calendar.HOUR_OF_DAY, -24) calendar.add(Calendar.HOUR_OF_DAY, -24)
return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, calendar.time.endOfDay()) return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and().lessThanOrEqualTo(fieldName, calendar.time.endOfDay())
} }
is PastDay -> {
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 -> { is DuringThisWeek -> {
val startDate = Date() val startDate = Date()
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
@ -584,8 +649,6 @@ sealed class QueryCondition : FilterElementRow {
get() { get() {
return when (this) { return when (this) {
is PastDay -> RowViewType.TITLE_VALUE_CHECK.ordinal is PastDay -> RowViewType.TITLE_VALUE_CHECK.ordinal
is LastGame -> RowViewType.TITLE_VALUE_CHECK.ordinal
is LastSession -> RowViewType.TITLE_VALUE_CHECK.ordinal
else -> { else -> {
when (this.operator) { when (this.operator) {
Operator.MORE -> RowViewType.TITLE_VALUE_CHECK.ordinal Operator.MORE -> RowViewType.TITLE_VALUE_CHECK.ordinal
@ -600,8 +663,6 @@ sealed class QueryCondition : FilterElementRow {
get() { get() {
return when (this) { return when (this) {
is PastDay -> BottomSheetType.EDIT_TEXT is PastDay -> BottomSheetType.EDIT_TEXT
is LastGame -> BottomSheetType.EDIT_TEXT
is LastSession -> BottomSheetType.EDIT_TEXT
else -> { else -> {
when (this.operator) { when (this.operator) {
Operator.MORE -> BottomSheetType.EDIT_TEXT Operator.MORE -> BottomSheetType.EDIT_TEXT
@ -630,8 +691,6 @@ sealed class QueryCondition : FilterElementRow {
is IsWeekDay -> R.string.week_days is IsWeekDay -> R.string.week_days
is IsWeekEnd -> R.string.weekend is IsWeekEnd -> R.string.weekend
is PastDay -> R.string.period_in_days is PastDay -> R.string.period_in_days
is LastGame -> R.string.last_records
is LastSession -> R.string.last_sessions
is NetAmountWon -> { is NetAmountWon -> {
when (this.operator) { when (this.operator) {
Operator.MORE -> R.string.won_amount_more_than Operator.MORE -> R.string.won_amount_more_than

@ -121,4 +121,9 @@ interface Deletable : Identifiable {
*/ */
fun getFailedDeleteMessage(status: DeleteValidityStatus): Int fun getFailedDeleteMessage(status: DeleteValidityStatus): Int
/**
* A method to override if we need to delete linked objects or other stuff
*/
fun deleteDependencies() {}
} }

@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.migrations
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.RealmMigration import io.realm.RealmMigration
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.CustomFieldEntry import net.pokeranalytics.android.model.realm.CustomFieldEntry
import timber.log.Timber import timber.log.Timber
@ -104,15 +105,16 @@ class PokerAnalyticsMigration : RealmMigration {
} }
schema.create("CustomFieldEntry")?.let { schema.create("CustomFieldEntry")?.let {
it.addField("id", String::class.java).setRequired("id", true) it.addField("id", String::class.java).setRequired("id", true)
it.addField("value", String::class.java).setNullable("type", false) it.addField("value", String::class.java).setNullable("value", false)
it.addField("order", Integer::class.java).setNullable("type", false) it.addField("order", Integer::class.java).setNullable("order", false)
it.addField("customField", CustomField::class.java).setNullable("customField", false)
} }
schema.get("CustomField")?.let { schema.get("CustomField")?.let {
it.addField("type", Integer::class.java).setNullable("type", false) it.addField("type", Integer::class.java).setNullable("type", false)
it.addField("duplicateValue", Boolean::class.java) it.addField("duplicateValue", Boolean::class.java)
it.addField("sortCondition", Integer::class.java)
it.addRealmListField("entries", CustomFieldEntry::class.java) it.addRealmListField("entries", CustomFieldEntry::class.java)
} }
schema.get("ReportSetup")?.let { schema.get("ReportSetup")?.let {
it.addRealmListField("statIds", Int::class.java) it.addRealmListField("statIds", Int::class.java)
it.addRealmListField("criteriaIds", Int::class.java) it.addRealmListField("criteriaIds", Int::class.java)
@ -121,12 +123,15 @@ class PokerAnalyticsMigration : RealmMigration {
it.addRealmObjectField("filter", filterSchema) it.addRealmObjectField("filter", filterSchema)
} }
} }
schema.get("Session")?.let { schema.get("Session")?.let {
it.addField("startDateHourMinuteComponent", Double::class.java).setNullable("startDateHourMinuteComponent", true) it.addField("startDateHourMinuteComponent", Double::class.java).setNullable("startDateHourMinuteComponent", true)
it.addField("endDateHourMinuteComponent", Double::class.java).setNullable("endDateHourMinuteComponent", true) it.addField("endDateHourMinuteComponent", Double::class.java).setNullable("endDateHourMinuteComponent", true)
it.addRealmListField("customFieldEntries", CustomFieldEntry::class.java)
} }
currentVersion++
schema.get("Filter")?.addField("filterableTypeOrdinal", Integer::class.java)
currentVersion++
} }
} }

@ -1,33 +1,42 @@
package net.pokeranalytics.android.model.realm package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import android.text.InputType
import io.realm.Realm import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import timber.log.Timber import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {
enum class Type(var resId: Int) { enum class Type(override var uniqueIdentifier: Int, var resId: Int) : IntIdentifiable {
LIST(R.string.enum_custom_field_type), LIST(0, R.string.enum_custom_field_type),
NUMBER(R.string.number), NUMBER(1, R.string.number),
AMOUNT(R.string.amount) AMOUNT(2, R.string.amount)
}
enum class Sort(override var uniqueIdentifier: Int) : IntIdentifiable {
DEFAULT(0),
ASCENDING(1),
DESCENDING(2)
} }
@PrimaryKey @PrimaryKey
@ -37,7 +46,7 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
override var name: String = "" override var name: String = ""
// Migration // Migration
var type: Int = Type.LIST.ordinal var type: Int = Type.LIST.uniqueIdentifier
set(value) { set(value) {
field = value field = value
this.updateRowRepresentation() this.updateRowRepresentation()
@ -45,20 +54,32 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
var duplicateValue: Boolean = false var duplicateValue: Boolean = false
var entries: RealmList<CustomFieldEntry> = RealmList() var entries: RealmList<CustomFieldEntry> = RealmList()
var sortCondition: Int = Sort.DEFAULT.uniqueIdentifier
set(value) {
field = value
sortEntries()
updateRowRepresentation()
}
// @todo @Ignore
private var entriesToDelete: ArrayList<CustomFieldEntry> = ArrayList()
override fun getDisplayName(context: Context): String {
return this.name
}
@Ignore @Ignore
override val viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal override var viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal
@Ignore @Ignore
private var rowRepresentation: List<RowRepresentable> = mutableListOf() private var rowRepresentation: List<RowRepresentable> = mutableListOf()
override fun localizedTitle(context: Context): String {
return this.name
}
override fun getDisplayName(context: Context): String {
return this.name
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return rowRepresentation return rowRepresentation
} }
@ -72,20 +93,21 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
} }
override fun isValidForSave(): Boolean { override fun isValidForSave(): Boolean {
return true return super.isValidForSave()
}
override fun alreadyExists(realm: Realm): Boolean {
return realm.where(this::class.java).equalTo("name", this.name).and().notEqualTo("id", this.id).findAll().isNotEmpty()
} }
override fun getFailedSaveMessage(status: SaveValidityStatus): Int { override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
return when (status) { return when (status) {
SaveValidityStatus.DATA_INVALID -> R.string.cf_empty_field_error SaveValidityStatus.DATA_INVALID -> R.string.cf_empty_field_error
SaveValidityStatus.ALREADY_EXISTS -> R.string.duplicate_cf_error
else -> super.getFailedSaveMessage(status) else -> super.getFailedSaveMessage(status)
} }
} }
override fun alreadyExists(realm: Realm): Boolean {
return realm.where(this::class.java).equalTo("name", this.name).and().notEqualTo("id", this.id).findAll().isNotEmpty()
}
override fun isValidForDelete(realm: Realm): Boolean { override fun isValidForDelete(realm: Realm): Boolean {
return true return true
} }
@ -95,6 +117,21 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
return R.string.relationship_error return R.string.relationship_error
} }
override val bottomSheetType: BottomSheetType
get() {
return when (type) {
Type.LIST.uniqueIdentifier -> BottomSheetType.LIST_STATIC
else -> BottomSheetType.EDIT_TEXT
}
}
override fun deleteDependencies() {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? { override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) { return when (row) {
is CustomFieldEntry -> row.editingDescriptors( is CustomFieldEntry -> row.editingDescriptors(
@ -106,43 +143,99 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
} }
} }
override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowRepresentableEditDescriptor>? {
return when (type) {
Type.LIST.uniqueIdentifier -> {
val defaultValue: Any? by map
val data: RealmList<CustomFieldEntry>? by map
arrayListOf(
RowRepresentableEditDescriptor(defaultValue, staticData = data)
)
}
else -> {
val defaultValue: String? by map
arrayListOf(
RowRepresentableEditDescriptor(
defaultValue, inputType = InputType.TYPE_CLASS_NUMBER
or InputType.TYPE_NUMBER_FLAG_DECIMAL
or InputType.TYPE_NUMBER_FLAG_SIGNED
)
)
}
}
}
/**
* Update the row representation
*/
private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> { private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME) rows.add(SimpleRow.NAME)
rows.add(CustomFieldRow.TYPE) rows.add(CustomFieldRow.TYPE)
if (type == Type.LIST.ordinal && entries.size >= 0) { if (type == Type.LIST.ordinal && entries.size >= 0) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.items_list))
if (entries.isNotEmpty()) { if (entries.isNotEmpty()) {
Timber.d("entries: ${entries.size}") rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.items_list))
entries.sortBy { it.order } sortEntries()
entries.forEach { customFieldEntry ->
customFieldEntry.isMovable = sortCondition == Sort.DEFAULT.uniqueIdentifier
}
rows.addAll(entries) rows.addAll(entries)
} }
} }
Timber.d("rows: ${rows.size}")
return rows return rows
} }
/**
* Sort the entries element
*/
private fun sortEntries() {
when (sortCondition) {
Sort.ASCENDING.uniqueIdentifier -> entries.sortBy { it.value }
Sort.DESCENDING.uniqueIdentifier -> entries.sortByDescending { it.value }
}
entries.forEachIndexed { index, customFieldEntry ->
customFieldEntry.order = index
}
}
fun updateRowRepresentation() { fun updateRowRepresentation() {
this.rowRepresentation = this.updatedRowRepresentationForCurrentState() this.rowRepresentation = this.updatedRowRepresentationForCurrentState()
} }
/** /**
* Add entry * Add an entry
*/ */
fun addEntry() { fun addEntry(): CustomFieldEntry {
val entry = CustomFieldEntry() val entry = CustomFieldEntry()
entry.order = (entries.lastOrNull()?.order ?: 0) + 1 entry.customField = this
entries.add(entry) entries.add(entry)
sortEntries()
updateRowRepresentation() updateRowRepresentation()
return entry
} }
/** /**
* * Delete an entry
*/ */
fun deleteEntry() { fun deleteEntry(entry: CustomFieldEntry) {
entries.remove(entry)
entriesToDelete.add(entry)
sortEntries()
updateRowRepresentation()
}
/**
* Remove the deleted entries from realm
*/
fun cleanDeletedEntries(realm: Realm) {
realm.executeTransaction {
entriesToDelete.forEach {
realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
}
entriesToDelete.clear()
}
} }
} }

@ -2,27 +2,51 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import android.text.InputType import android.text.InputType
import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.toCurrency
import java.util.* import java.util.*
import java.util.Currency
open class CustomFieldEntry : RealmObject(), RowRepresentable { open class CustomFieldEntry : RealmObject(), Manageable, RowRepresentable {
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
var value: String = ""
var order: Int = 0 var order: Int = 0
var customField: CustomField? = null
var value: String = ""
@Ignore
var isMovable: Boolean = false
@Ignore @Ignore
override val viewType: Int = RowViewType.TITLE.ordinal override val viewType: Int = RowViewType.TITLE_VALUE_ACTION.ordinal
override val imageRes: Int?
get() {
return if (isMovable) R.drawable.ic_reorder else null
}
override val imageTint: Int?
get() {
return R.color.kaki
}
@Ignore @Ignore
override val bottomSheetType: BottomSheetType = BottomSheetType.EDIT_TEXT override val bottomSheetType: BottomSheetType = BottomSheetType.EDIT_TEXT
@ -42,4 +66,56 @@ open class CustomFieldEntry : RealmObject(), RowRepresentable {
) )
} }
override fun isValidForSave(): Boolean {
return true
}
override fun alreadyExists(realm: Realm): Boolean {
return realm.where(this::class.java).notEqualTo("id", this.id).findAll().isNotEmpty()
}
override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
throw ModelException("${this::class.java} getFailedSaveMessage for $status not handled")
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.cf_entry_delete_popup_message
}
override fun deleteDependencies() {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
override fun updateValue(value: Any?, row: RowRepresentable) {
this.value = value as String? ?: ""
}
override fun isValidForDelete(realm: Realm): Boolean {
if (realm.where<Session>().contains("customFieldEntries.id", id).findAll().isNotEmpty()) {
return false
}
return true
}
/**
* Return the amount
*/
fun getFormattedValue(currency: Currency? = null): String {
return when (customField?.type) {
CustomField.Type.AMOUNT.uniqueIdentifier -> {
try {
value.toDouble().toCurrency(currency)
} catch (e: Exception) {
NULL_TEXT
}
}
else -> {
value
}
}
}
} }

@ -8,6 +8,7 @@ import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import java.util.* import java.util.*
@ -22,8 +23,9 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable {
companion object { companion object {
// Create a new instance // Create a new instance
fun newInstance(realm: Realm): Filter { fun newInstance(realm: Realm, filterableType:Int): Filter {
val filter = Filter() val filter = Filter()
filter.filterableTypeOrdinal = filterableType
return realm.copyToRealm(filter) return realm.copyToRealm(filter)
} }
@ -60,6 +62,16 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable {
var filterConditions: RealmList<FilterCondition> = RealmList() var filterConditions: RealmList<FilterCondition> = RealmList()
private set private set
private var filterableTypeOrdinal: Int? = null
val filterableType: FilterableType
get() {
this.filterableTypeOrdinal?.let {
return FilterableType.values()[it]
}
return FilterableType.ALL
}
fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) { fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) {
println("list of querys saving: ${filterConditionRows.map { it.id }}") println("list of querys saving: ${filterConditionRows.map { it.id }}")
println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}") println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")

@ -80,7 +80,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
AnyTableSize::class.java -> "tableSize" AnyTableSize::class.java -> "tableSize"
AnyTournamentType::class.java -> "tournamentType" AnyTournamentType::class.java -> "tournamentType"
AnyBlind::class.java -> "blinds" AnyBlind::class.java -> "blinds"
NumberOfTable::class.java -> "numberOfTable" NumberOfTable::class.java -> "numberOfTables"
NetAmountWon::class.java, NetAmountLost::class.java -> "computableResults.ratedNet" NetAmountWon::class.java, NetAmountLost::class.java -> "computableResults.ratedNet"
NumberOfRebuy::class.java -> "result.numberOfRebuy" NumberOfRebuy::class.java -> "result.numberOfRebuy"
TournamentNumberOfPlayer::class.java -> "result.tournamentNumberOfPlayers" TournamentNumberOfPlayer::class.java -> "result.tournamentNumberOfPlayers"
@ -91,7 +91,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
AnyDayOfWeek::class.java, IsWeekEnd::class.java, IsWeekDay::class.java -> "dayOfWeek" AnyDayOfWeek::class.java, IsWeekEnd::class.java, IsWeekDay::class.java -> "dayOfWeek"
AnyMonthOfYear::class.java -> "month" AnyMonthOfYear::class.java -> "month"
AnyYear::class.java -> "year" AnyYear::class.java -> "year"
IsToday::class.java, WasYesterday::class.java, WasTodayAndYesterday::class.java, DuringThisYear::class.java, DuringThisMonth::class.java, DuringThisWeek::class.java -> "startDate" PastDay::class.java, IsToday::class.java, WasYesterday::class.java, WasTodayAndYesterday::class.java, DuringThisYear::class.java, DuringThisMonth::class.java, DuringThisWeek::class.java -> "startDate"
StartedFromTime::class.java -> "startDateHourMinuteComponent" StartedFromTime::class.java -> "startDateHourMinuteComponent"
EndedToTime::class.java -> "endDateHourMinuteComponent" EndedToTime::class.java -> "endDateHourMinuteComponent"
Duration::class.java -> "netDuration" Duration::class.java -> "netDuration"
@ -125,25 +125,25 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
override var year: Int? = null override var year: Int? = null
override var dayOfMonth: Int? = null override var dayOfMonth: Int? = null
private var startDateHourMinuteComponent: Double? = null private var startDateHourMinuteComponent: Double? = null
get() { get() {
if (field == null && startDate != null) { if (field == null && startDate != null) {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = startDate cal.time = startDate
field = cal.hourMinute() field = cal.hourMinute()
} }
return field return field
} }
private var endDateHourMinuteComponent: Double? = null private var endDateHourMinuteComponent: Double? = null
get() { get() {
if (field == null && endDate != null) { if (field == null && endDate != null) {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = endDate cal.time = endDate
field = cal.hourMinute() field = cal.hourMinute()
} }
return field return field
} }
/** /**
* The start date of the session * The start date of the session
@ -151,13 +151,13 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
var startDate: Date? = null var startDate: Date? = null
set(value) { set(value) {
field = value field = value
if (field == null) { if (field == null) {
startDateHourMinuteComponent = null startDateHourMinuteComponent = null
} else { } else {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = field cal.time = field
startDateHourMinuteComponent = cal.hourMinute() startDateHourMinuteComponent = cal.hourMinute()
} }
this.updateTimeParameter(field) this.updateTimeParameter(field)
this.computeNetDuration() this.computeNetDuration()
@ -176,15 +176,15 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
var endDate: Date? = null var endDate: Date? = null
set(value) { set(value) {
field = value field = value
if (field == null) { if (field == null) {
endDateHourMinuteComponent = null endDateHourMinuteComponent = null
} else { } else {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = field cal.time = field
endDateHourMinuteComponent = cal.hourMinute() endDateHourMinuteComponent = cal.hourMinute()
} }
this.computeNetDuration() this.computeNetDuration()
this.dateChanged() this.dateChanged()
this.defineDefaultTournamentBuyinIfNecessary() this.defineDefaultTournamentBuyinIfNecessary()
this.computeStats() this.computeStats()
@ -266,7 +266,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
field = value field = value
this.computeStats() this.computeStats()
formatBlinds() formatBlinds()
this.result?.computeNumberOfRebuy() this.result?.computeNumberOfRebuy()
} }
var blinds: String? = null var blinds: String? = null
@ -276,10 +276,10 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// The entry fee of the tournament // The entry fee of the tournament
var tournamentEntryFee: Double? = null var tournamentEntryFee: Double? = null
set(value) { set(value) {
field = value field = value
this.result?.computeNumberOfRebuy() this.result?.computeNumberOfRebuy()
} }
// The total number of players who participated in the tournament // The total number of players who participated in the tournament
var tournamentNumberOfPlayers: Int? = null var tournamentNumberOfPlayers: Int? = null
@ -293,6 +293,9 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// The features of the tournament, like Knockout, Shootout, Turbo... // The features of the tournament, like Knockout, Shootout, Turbo...
var tournamentFeatures: RealmList<TournamentFeature> = RealmList() var tournamentFeatures: RealmList<TournamentFeature> = RealmList()
// The custom fields values
var customFieldEntries: RealmList<CustomFieldEntry> = RealmList()
fun bankrollHasBeenUpdated() { fun bankrollHasBeenUpdated() {
formatBlinds() formatBlinds()
} }
@ -574,7 +577,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
return blinds ?: NULL_TEXT return blinds ?: NULL_TEXT
} }
fun formatBlinds() { fun formatBlinds() {
blinds = null blinds = null
if (cgBigBlind == null) return if (cgBigBlind == null) return
cgBigBlind?.let { bb -> cgBigBlind?.let { bb ->
@ -589,9 +592,11 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
* Delete the object from realm * Delete the object from realm
*/ */
fun delete() { fun delete() {
realm.executeTransaction { if (isValid) {
cleanup() realm.executeTransaction {
deleteFromRealm() cleanup()
deleteFromRealm()
}
} }
} }
@ -613,6 +618,16 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
@Ignore @Ignore
override val viewType: Int = RowViewType.ROW_SESSION.ordinal override val viewType: Int = RowViewType.ROW_SESSION.ordinal
// Override to surcharge custom field viewType
override fun viewTypeForPosition(position: Int): Int {
rowRepresentationForCurrentState[position].let {
if (it is CustomField) {
return RowViewType.TITLE_VALUE.ordinal
}
}
return super.viewTypeForPosition(position)
}
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
return "Session ${this.creationDate}" return "Session ${this.creationDate}"
} }
@ -678,6 +693,13 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// Rows // Rows
rows.addAll(SessionRow.getRows(this)) rows.addAll(SessionRow.getRows(this))
// Add custom fields
realm?.let {
rows.add(SeparatorRow())
rows.addAll(it.sorted<CustomField>())
}
return rows return rows
} }
@ -711,10 +733,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
SessionRow.START_DATE -> this.startDate?.shortDateTime() ?: NULL_TEXT SessionRow.START_DATE -> this.startDate?.shortDateTime() ?: NULL_TEXT
SessionRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT SessionRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT
SessionRow.TIPS -> result?.tips?.toCurrency(currency) ?: NULL_TEXT SessionRow.TIPS -> result?.tips?.toCurrency(currency) ?: NULL_TEXT
SessionRow.TOURNAMENT_TYPE -> this.tournamentType?.let { SessionRow.TOURNAMENT_TYPE -> {
TournamentType.values()[it].localizedTitle(context) this.tournamentType?.let {
} ?: run { TournamentType.values()[it].localizedTitle(context)
NULL_TEXT } ?: run {
NULL_TEXT
}
} }
SessionRow.TOURNAMENT_FEATURE -> { SessionRow.TOURNAMENT_FEATURE -> {
if (tournamentFeatures.size > 2) { if (tournamentFeatures.size > 2) {
@ -730,6 +754,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
} }
SessionRow.TOURNAMENT_NAME -> tournamentName?.name ?: NULL_TEXT SessionRow.TOURNAMENT_NAME -> tournamentName?.name ?: NULL_TEXT
is CustomField -> {
customFieldEntries.find { it.customField?.id == row.id }?.let { customFieldEntry ->
return customFieldEntry.getFormattedValue(currency)
}
return NULL_TEXT
}
else -> throw UnmanagedRowRepresentableException("Unmanaged row = ${row}") else -> throw UnmanagedRowRepresentableException("Unmanaged row = ${row}")
} }
} }
@ -837,6 +867,19 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
"tips" to result?.tips "tips" to result?.tips
) )
) )
is CustomField -> {
row.editingDescriptors(
when (row.type) {
CustomField.Type.LIST.uniqueIdentifier -> mapOf(
"defaultValue" to customFieldEntries.find { it.customField?.id == row.id }?.value,
"data" to row.entries
)
else -> mapOf(
"defaultValue" to customFieldEntries.find { it.customField?.id == row.id }?.value
)
}
)
}
else -> null else -> null
} }
} }
@ -946,7 +989,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
result = localResult result = localResult
} }
SessionRow.TOURNAMENT_NAME -> tournamentName = value as TournamentName? SessionRow.TOURNAMENT_NAME -> tournamentName = value as TournamentName?
SessionRow.TOURNAMENT_TYPE -> tournamentType = value as Int? SessionRow.TOURNAMENT_TYPE -> tournamentType = (value as TournamentType?)?.ordinal
SessionRow.TOURNAMENT_FEATURE -> { SessionRow.TOURNAMENT_FEATURE -> {
value?.let { value?.let {
@ -956,6 +999,28 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
tournamentFeatures.removeAll(this.tournamentFeatures) tournamentFeatures.removeAll(this.tournamentFeatures)
} }
} }
is CustomField -> {
customFieldEntries.filter { it.customField?.id == row.id }.let {
customFieldEntries.removeAll(it)
}
when (row.type) {
CustomField.Type.AMOUNT.uniqueIdentifier,
CustomField.Type.NUMBER.uniqueIdentifier -> {
if (value != null) {
val customFieldEntry = CustomFieldEntry()
customFieldEntry.customField = row
customFieldEntry.value = value.toString()
customFieldEntries.add(customFieldEntry)
}
}
CustomField.Type.LIST.uniqueIdentifier -> {
if (value != null && value is CustomFieldEntry) {
customFieldEntries.add(value)
}
}
}
}
} }
} }
@ -1013,7 +1078,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
style: GraphFragment.Style, style: GraphFragment.Style,
groupName: String, groupName: String,
context: Context context: Context
) : LegendContent { ): LegendContent {
when (style) { when (style) {
GraphFragment.Style.MULTILINE -> { GraphFragment.Style.MULTILINE -> {

@ -37,8 +37,12 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return when (queryCondition) { return when (queryCondition) {
QueryCondition.AnyBankroll::class.java -> "bankroll.id" QueryCondition.AnyBankroll::class.java -> "bankroll.id"
QueryCondition.AnyTransactionType::class.java -> "type" QueryCondition.AnyTransactionType::class.java -> "type.id"
QueryCondition.StartedFromDate::class.java, QueryCondition.StartedToDate::class.java -> "date" QueryCondition.StartedFromDate::class.java, QueryCondition.StartedToDate::class.java -> "date"
QueryCondition.AnyDayOfWeek::class.java, QueryCondition.IsWeekEnd::class.java, QueryCondition.IsWeekDay::class.java -> "dayOfWeek"
QueryCondition.AnyMonthOfYear::class.java -> "month"
QueryCondition.AnyYear::class.java -> "year"
QueryCondition.PastDay::class.java, QueryCondition.IsToday::class.java, QueryCondition.WasYesterday::class.java, QueryCondition.WasTodayAndYesterday::class.java, QueryCondition.DuringThisYear::class.java, QueryCondition.DuringThisMonth::class.java, QueryCondition.DuringThisWeek::class.java -> "date"
else -> null else -> null
} }
} }

@ -10,6 +10,7 @@ import net.pokeranalytics.android.ui.view.RowViewType
interface RowRepresentableDelegate { interface RowRepresentableDelegate {
fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean = false) {} fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean = false) {}
fun onRowValueChanged(value: Any?, row: RowRepresentable) {} fun onRowValueChanged(value: Any?, row: RowRepresentable) {}
fun onRowDeleted(row: RowRepresentable) {}
} }
/** /**
@ -54,10 +55,13 @@ class RowRepresentableAdapter(
return return
} }
/*
val index = this.dataSource.indexForRow(row) val index = this.dataSource.indexForRow(row)
if (index >= 0) { if (index >= 0) {
notifyItemChanged(index) notifyItemChanged(index)
} }
*/
notifyDataSetChanged()
} }
/** /**

@ -24,10 +24,6 @@ import net.pokeranalytics.android.util.billing.AppGuard
import java.io.File import java.io.File
// Sizes // Sizes
val Int.dp: Int val Int.dp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt() get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@ -70,8 +66,9 @@ fun PokerAnalyticsActivity.openPlayStorePage() {
} }
// Open email for "Contact us" // Open email for "Contact us"
fun PokerAnalyticsActivity.openContactMail(subjectStringRes: Int, filePath: String?= null) { fun PokerAnalyticsActivity.openContactMail(subjectStringRes: Int, filePath: String? = null) {
val info = "v${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE}) - ${AppGuard.isProUser}, Android ${android.os.Build.VERSION.SDK_INT}, ${DeviceUtils.getDeviceName()}" val info =
"v${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE}) - ${AppGuard.isProUser}, Android ${android.os.Build.VERSION.SDK_INT}, ${DeviceUtils.getDeviceName()}"
val emailIntent = Intent(Intent.ACTION_SEND) val emailIntent = Intent(Intent.ACTION_SEND)
@ -105,6 +102,7 @@ fun PokerAnalyticsActivity.openUrl(url: String) {
fun PokerAnalyticsActivity.showAlertDialog(title: Int? = null, message: Int? = null) { fun PokerAnalyticsActivity.showAlertDialog(title: Int? = null, message: Int? = null) {
showAlertDialog(this, title, message) showAlertDialog(this, title, message)
} }
fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = null) { fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = null) {
context?.let { context?.let {
showAlertDialog(it, title, message) showAlertDialog(it, title, message)
@ -114,7 +112,10 @@ fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = n
/** /**
* Create and show an alert dialog * Create and show an alert dialog
*/ */
fun showAlertDialog(context: Context, title: Int? = null, message: Int? = null) { fun showAlertDialog(
context: Context, title: Int? = null, message: Int? = null, showCancelButton: Boolean = false,
positiveAction: (() -> Unit)? = null, negativeAction: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
title?.let { title?.let {
builder.setTitle(title) builder.setTitle(title)
@ -122,7 +123,14 @@ fun showAlertDialog(context: Context, title: Int? = null, message: Int? = null)
message?.let { message?.let {
builder.setMessage(message) builder.setMessage(message)
} }
builder.setPositiveButton(net.pokeranalytics.android.R.string.ok, null) builder.setPositiveButton(net.pokeranalytics.android.R.string.ok) { _, _ ->
positiveAction?.invoke()
}
if (showCancelButton) {
builder.setNegativeButton(R.string.cancel) { _, _ ->
negativeAction?.invoke()
}
}
builder.show() builder.show()
} }

@ -137,8 +137,14 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate, FilterHandler {
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) { override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) { when (tab.position) {
0 -> recyclerView.adapter = feedSessionAdapter 0 -> {
1 -> recyclerView.adapter = feedTransactionAdapter currentFilterable = FilterableType.SESSION
recyclerView.adapter = feedSessionAdapter
}
1 -> {
currentFilterable = FilterableType.TRANSACTION
recyclerView.adapter = feedTransactionAdapter
}
} }
} }
@ -161,27 +167,42 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate, FilterHandler {
betaLimitDate = sdf.parse("17/7/2019 10:00") betaLimitDate = sdf.parse("17/7/2019 10:00")
val filter : Filter? = this.currentFilter(this.requireContext(), getRealm()) val filter : Filter? = this.currentFilter(this.requireContext(), getRealm())
val sessionFilter: Filter? = filter?.let {
if (it.filterableType == FilterableType.SESSION) {
it
} else {
null
}
}
val transactionFilter: Filter? = filter?.let {
if (it.filterableType == FilterableType.TRANSACTION) {
it
} else {
null
}
}
// Sessions // Sessions
this.realmSessions = filter?.results() ?: run { getRealm().where<Session>().findAll() }.sort("startDate", Sort.DESCENDING) this.realmSessions = sessionFilter?.results() ?: run { getRealm().where<Session>().findAll() }.sort("startDate", Sort.DESCENDING)
this.realmSessions.addChangeListener { _, _ -> this.realmSessions.addChangeListener { _, _ ->
this.feedSessionAdapter.refreshData() this.feedSessionAdapter.refreshData()
this.feedSessionAdapter.notifyDataSetChanged() this.feedSessionAdapter.notifyDataSetChanged()
} }
val pendingSessions = filter?.let { getRealm().where<Session>().alwaysFalse().findAll() } ?: run { getRealm().where<Session>().isNull("year").isNull("month").findAll().sort("startDate", Sort.DESCENDING) } val pendingSessions = sessionFilter?.let { getRealm().where<Session>().alwaysFalse().findAll() } ?: run { getRealm().where<Session>().isNull("year").isNull("month").findAll().sort("startDate", Sort.DESCENDING) }
val distinctDateSessions = filter?.results("year", "month") ?: run { getRealm().where<Session>().distinct("year", "month").findAll() }.sort("startDate", Sort.DESCENDING) val distinctDateSessions = sessionFilter?.results("year", "month") ?: run { getRealm().where<Session>().distinct("year", "month").findAll() }.sort("startDate", Sort.DESCENDING)
this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(this, realmSessions, pendingSessions, distinctDateSessions) this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(this, realmSessions, pendingSessions, distinctDateSessions)
// Transactions // Transactions
this.realmTransactions = getRealm().where<Transaction>().findAll().sort("date", Sort.DESCENDING) this.realmTransactions = transactionFilter?.results() ?: run { getRealm().where<Transaction>().findAll()}.sort("date", Sort.DESCENDING)
this.realmTransactions.addChangeListener { _, _ -> this.realmTransactions.addChangeListener { _, _ ->
this.feedTransactionAdapter.refreshData() this.feedTransactionAdapter.refreshData()
this.feedTransactionAdapter.notifyDataSetChanged() this.feedTransactionAdapter.notifyDataSetChanged()
} }
val distinctDateTransactions = getRealm().where<Transaction>().distinct("year", "month").findAll().sort("date", Sort.DESCENDING) val distinctDateTransactions = transactionFilter?.results("year", "month") ?: run { getRealm().where<Transaction>().distinct("year", "month").findAll() }.sort("date", Sort.DESCENDING)
this.feedTransactionAdapter = FeedTransactionRowRepresentableAdapter(this, realmTransactions, distinctDateTransactions) this.feedTransactionAdapter = FeedTransactionRowRepresentableAdapter(this, realmTransactions, distinctDateTransactions)
val viewManager = SmoothScrollLinearLayoutManager(requireContext()) val viewManager = SmoothScrollLinearLayoutManager(requireContext())

@ -158,7 +158,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
isUpdating = true isUpdating = true
} ?: run { } ?: run {
realm.beginTransaction() realm.beginTransaction()
currentFilter = Filter.newInstance(realm) currentFilter = Filter.newInstance(realm, this.filterableType.ordinal)
realm.commitTransaction() realm.commitTransaction()
} }

@ -31,10 +31,16 @@ import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.billing.IAPProducts import net.pokeranalytics.android.util.billing.IAPProducts
import net.pokeranalytics.android.util.billing.PurchaseDelegate import net.pokeranalytics.android.util.billing.PurchaseDelegate
import java.lang.ref.WeakReference
import java.time.Period import java.time.Period
class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListener, PurchaseDelegate, ViewPager.OnPageChangeListener { class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListener, PurchaseDelegate, ViewPager.OnPageChangeListener {
companion object {
val parallax: Float = 64f.px
}
private var pagerAdapter: ScreenSlidePagerAdapter? = null
private var selectedProduct: SkuDetails? = null private var selectedProduct: SkuDetails? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -85,7 +91,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
// Pager // Pager
// The pager adapter, which provides the pages to the view pager widget. // The pager adapter, which provides the pages to the view pager widget.
val pagerAdapter = ScreenSlidePagerAdapter(requireFragmentManager()) this.pagerAdapter = ScreenSlidePagerAdapter(requireFragmentManager())
this.pager.adapter = pagerAdapter this.pager.adapter = pagerAdapter
this.pager.addOnPageChangeListener(this) this.pager.addOnPageChangeListener(this)
@ -108,7 +114,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
this.pageIndicator.addView(view, layoutParam) this.pageIndicator.addView(view, layoutParam)
} }
this.changeColorOfIndicator(0) this.updatePagerIndicators(0)
} }
@ -118,13 +124,21 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
*/ */
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
private var fragments: HashMap<Int, WeakReference<ScreenSlidePageFragment>> = HashMap()
private inner class FeatureDescriptor(var iconResId: Int, var titleResId: Int, var descResId: Int) private inner class FeatureDescriptor(var iconResId: Int, var titleResId: Int, var descResId: Int)
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position) as ScreenSlidePageFragment
fragments[position] = WeakReference(fragment)
return super.instantiateItem(container, position)
}
private val dataSource: List<FeatureDescriptor> = listOf( private val dataSource: List<FeatureDescriptor> = listOf(
FeatureDescriptor(R.drawable.ic_baseline_all_inclusive_24px, R.string.f_unlimited, R.string.f_unlimited_desc), FeatureDescriptor(R.drawable.ic_baseline_all_inclusive, R.string.f_unlimited, R.string.f_unlimited_desc),
FeatureDescriptor(R.drawable.ic_baseline_wifi_off_24px, R.string.f_offline, R.string.f_offline_desc), FeatureDescriptor(R.drawable.ic_baseline_wifi_off, R.string.f_offline, R.string.f_offline_desc),
FeatureDescriptor(R.drawable.ic_baseline_vpn_key_24px, R.string.f_privacy, R.string.f_privacy_desc), FeatureDescriptor(R.drawable.ic_baseline_vpn_key, R.string.f_privacy, R.string.f_privacy_desc),
FeatureDescriptor(R.drawable.ic_baseline_email_24px, R.string.f_support, R.string.f_support_desc) FeatureDescriptor(R.drawable.ic_baseline_email, R.string.f_support, R.string.f_support_desc)
) )
override fun getCount(): Int = this.dataSource.size override fun getCount(): Int = this.dataSource.size
@ -133,21 +147,27 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
return ScreenSlidePageFragment(d.iconResId, d.titleResId, d.descResId) return ScreenSlidePageFragment(d.iconResId, d.titleResId, d.descResId)
} }
/**
* Return the fragment at [position]
*/
fun getFragment(position: Int): ScreenSlidePageFragment? {
if (fragments.contains(position)) {
return fragments[position]?.get()
}
return null
}
} }
// SkuDetailsResponseListener // SkuDetailsResponseListener
override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) { override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) {
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) { if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) {
this.hideLoader() this.hideLoader()
selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier } selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier }
updateUI() updateUI()
} }
} }
private fun updateUI() { private fun updateUI() {
this.selectedProduct?.let { this.selectedProduct?.let {
this.purchase.isEnabled = true this.purchase.isEnabled = true
val perYearString = requireContext().getString(R.string.year_subscription) val perYearString = requireContext().getString(R.string.year_subscription)
@ -162,9 +182,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
val formattedFreeTrial = val formattedFreeTrial =
"$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial) "$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial)
this.freetrial.text = formattedFreeTrial this.freetrial.text = formattedFreeTrial
} }
} }
// PurchaseDelegate // PurchaseDelegate
@ -182,22 +200,30 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
pagerAdapter?.getFragment(position)?.let {
it.updateViewsPosition(-positionOffset * parallax)
}
pagerAdapter?.getFragment(position + 1)?.let {
it.updateViewsPosition((1 - positionOffset) * parallax)
}
}
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
this.changeColorOfIndicator(position) updatePagerIndicators(position)
} }
private fun changeColorOfIndicator(position: Int) { private fun updatePagerIndicators(position: Int) {
this.pageIndicator.children.forEachIndexed { index, view -> this.pageIndicator.children.forEachIndexed { index, view ->
val drawable = view.background val drawable = view.background
when (drawable) { when (drawable) {
is GradientDrawable -> { is GradientDrawable -> {
val color = if (position == index) R.color.white else R.color.quantum_grey val color = if (position == index) R.color.white else R.color.quantum_grey
drawable.setColor(requireContext().getColor(color)) drawable.setColor(requireContext().getColor(color))
} else -> {} }
else -> {
}
} }
} }
} }

@ -31,6 +31,7 @@ abstract class DeletableItemFragment : RealmFragment() {
lateinit var dataListAdapter: RowRepresentableAdapter lateinit var dataListAdapter: RowRepresentableAdapter
private var deletedItem: RealmObject? = null private var deletedItem: RealmObject? = null
private var itemHasBeenReInserted: Boolean = false
private var lastDeletedItemPosition: Int = 0 private var lastDeletedItemPosition: Int = 0
private var mainLayout: ViewGroup? = null private var mainLayout: ViewGroup? = null
@ -91,8 +92,10 @@ abstract class DeletableItemFragment : RealmFragment() {
deletedItem = getRealm().copyFromRealm(itemToDelete) deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition lastDeletedItemPosition = itemPosition
getRealm().executeTransaction { getRealm().executeTransaction {
itemToDelete.deleteDependencies()
itemToDelete.deleteFromRealm() itemToDelete.deleteFromRealm()
} }
itemHasBeenReInserted = false
updateUIAfterDeletion(itemPosition) updateUIAfterDeletion(itemPosition)
showUndoSnackBar() showUndoSnackBar()
} else { } else {
@ -115,10 +118,13 @@ abstract class DeletableItemFragment : RealmFragment() {
this.mainLayout?.let { view -> this.mainLayout?.let { view ->
snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE) snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
snackBar?.setAction(R.string.cancel) { snackBar?.setAction(R.string.cancel) {
getRealm().executeTransaction { realm -> if (!itemHasBeenReInserted) {
deletedItem?.let { itemHasBeenReInserted = true
val item = realm.copyToRealmOrUpdate(it) getRealm().executeTransaction { realm ->
updateUIAfterUndoDeletion(item) deletedItem?.let {
val item = realm.copyToRealmOrUpdate(it)
updateUIAfterUndoDeletion(item)
}
} }
} }
} }

@ -22,6 +22,15 @@ class ScreenSlidePageFragment(var iconResId: Int, var titleResId: Int, var descr
this.icon.setImageResource(this.iconResId) this.icon.setImageResource(this.iconResId)
this.title.text = requireContext().getString(this.titleResId) this.title.text = requireContext().getString(this.titleResId)
this.description.text = requireContext().getString(this.descriptionResId) this.description.text = requireContext().getString(this.descriptionResId)
}
/**
* Update views position
*/
fun updateViewsPosition(position: Float) {
view?.findViewById<View>(R.id.title)?.translationX = position
view?.findViewById<View>(R.id.icon)?.translationX = position
view?.findViewById<View>(R.id.description)?.translationX = position * 2f
} }

@ -32,6 +32,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
var currentCurrency: Currency? = null var currentCurrency: Currency? = null
private var isClearable: Boolean = true private var isClearable: Boolean = true
private var isDeletable: Boolean = false
private var rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>? = null private var rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>? = null
companion object { companion object {
@ -44,7 +45,8 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
delegate: RowRepresentableDelegate, delegate: RowRepresentableDelegate,
rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>?, rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>?,
isClearable: Boolean? = true, isClearable: Boolean? = true,
currentCurrency: Currency? = null currentCurrency: Currency? = null,
isDeletable: Boolean? = false
): BottomSheetFragment { ): BottomSheetFragment {
val bottomSheetFragment = row.bottomSheetType.newInstance() val bottomSheetFragment = row.bottomSheetType.newInstance()
bottomSheetFragment.show(fragmentManager, "bottomSheet") bottomSheetFragment.show(fragmentManager, "bottomSheet")
@ -52,6 +54,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
bottomSheetFragment.delegate = delegate bottomSheetFragment.delegate = delegate
bottomSheetFragment.rowRepresentableEditDescriptors = rowRepresentableEditDescriptors bottomSheetFragment.rowRepresentableEditDescriptors = rowRepresentableEditDescriptors
bottomSheetFragment.isClearable = isClearable ?: true bottomSheetFragment.isClearable = isClearable ?: true
bottomSheetFragment.isDeletable = isDeletable ?: true
bottomSheetFragment.currentCurrency = currentCurrency bottomSheetFragment.currentCurrency = currentCurrency
return bottomSheetFragment return bottomSheetFragment
} }
@ -111,6 +114,13 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
dismiss() dismiss()
true true
} }
bottomSheetToolbar.menu.findItem(R.id.actionDelete).setOnMenuItemClickListener {
delegate.onRowDeleted(row)
dismiss()
true
}
bottomSheetToolbar.menu.findItem(R.id.actionAdd).setOnMenuItemClickListener { bottomSheetToolbar.menu.findItem(R.id.actionAdd).setOnMenuItemClickListener {
val liveData = when (row) { val liveData = when (row) {
SessionRow.GAME -> LiveData.GAME SessionRow.GAME -> LiveData.GAME
@ -137,6 +147,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
} }
bottomSheetToolbar.menu.findItem(R.id.actionClear).isVisible = isClearable bottomSheetToolbar.menu.findItem(R.id.actionClear).isVisible = isClearable
bottomSheetToolbar.menu.findItem(R.id.actionDelete).isVisible = isDeletable
} }
} }

@ -8,7 +8,6 @@ import kotlinx.android.synthetic.main.bottom_sheet_list.*
import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.* import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
import net.pokeranalytics.android.model.TournamentType
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -36,7 +35,7 @@ class BottomSheetStaticListFragment : BottomSheetFragment(), StaticRowRepresenta
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
this.delegate.onRowValueChanged((row as TournamentType).ordinal, this.row) this.delegate.onRowValueChanged(row, this.row)
dismiss() dismiss()
super.onRowSelected(position, row, fromAction) super.onRowSelected(position, row, fromAction)
} }

@ -7,13 +7,18 @@ import android.view.ViewGroup
import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.ChipGroup
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_custom_view.* import kotlinx.android.synthetic.main.fragment_custom_view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.CustomField import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.CustomFieldEntry import net.pokeranalytics.android.model.realm.CustomFieldEntry
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.showAlertDialog
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -22,6 +27,7 @@ import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList
/** /**
* Custom EditableDataFragment to manage the Transaction data * Custom EditableDataFragment to manage the Transaction data
@ -34,8 +40,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
return this.item as CustomField return this.item as CustomField
} }
private val oldRows: ArrayList<RowRepresentable> = ArrayList() private val deletedCustomFieldEntries: ArrayList<CustomFieldEntry> = ArrayList()
private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
@ -54,6 +59,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
} }
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
if (target.adapterPosition <= 2) { if (target.adapterPosition <= 2) {
return false return false
} }
@ -71,7 +77,15 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
return true return true
} }
override fun onMoved(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, fromPos: Int, target: RecyclerView.ViewHolder, toPos: Int, x: Int, y: Int) { override fun onMoved(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
fromPos: Int,
target: RecyclerView.ViewHolder,
toPos: Int,
x: Int,
y: Int
) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y) super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
Collections.swap(customField.entries, fromPos - (CustomFieldRow.values().size + 1), toPos - (CustomFieldRow.values().size + 1)) Collections.swap(customField.entries, fromPos - (CustomFieldRow.values().size + 1), toPos - (CustomFieldRow.values().size + 1))
@ -101,7 +115,6 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
//shouldOpenKeyboard = false
initUI() initUI()
} }
@ -146,27 +159,50 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
when (row) { when (row) {
is CustomFieldEntry -> { is CustomFieldEntry -> {
val data = customField.editDescriptors(row) val data = customField.editDescriptors(row)
Timber.d("data: $data") BottomSheetFragment.create(fragmentManager, row, this, data, isClearable = false, isDeletable = true)
BottomSheetFragment.create(fragmentManager, row, this, data, false)
} }
else -> super.onRowSelected(position, row, fromAction) else -> super.onRowSelected(position, row, fromAction)
} }
} }
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
Timber.d("onRowValueChanged: $row => $value")
when (row) { when (row) {
is CustomFieldEntry -> { is CustomFieldEntry -> {
row.value = value as String? ?: "" row.updateValue(value, row)
rowRepresentableAdapter.refreshRow(row) customField.updateRowRepresentation()
rowRepresentableAdapter.notifyDataSetChanged()
} }
CustomFieldRow.TYPE -> { CustomFieldRow.TYPE -> {
customField.updateValue(value, row) customField.updateValue(value, row)
updateUI() updateUI()
updateAdapterUI() rowRepresentableAdapter.notifyDataSetChanged()
} }
else -> super.onRowValueChanged(value, row)
} }
}
override fun onRowDeleted(row: RowRepresentable) {
super.onRowDeleted(row)
when (row) {
is CustomFieldEntry -> {
if (!row.isValidForDelete(getRealm())) {
val status = row.getDeleteStatus(getRealm())
val message = row.getFailedDeleteMessage(status)
showAlertDialog(requireContext(), R.string.cf_entry_delete_popup_title, message, showCancelButton = true, positiveAction = {
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
})
return
}
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
}
}
}
override fun onDataSaved() {
super.onDataSaved()
customField.cleanDeletedEntries(getRealm())
} }
/** /**
@ -177,17 +213,68 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
bottomBar.translationY = 72f.px bottomBar.translationY = 72f.px
bottomBar.visibility = View.VISIBLE bottomBar.visibility = View.VISIBLE
itemTouchHelper.attachToRecyclerView(recyclerView) if (customField.sortCondition == CustomField.Sort.DEFAULT.uniqueIdentifier) {
itemTouchHelper.attachToRecyclerView(recyclerView)
} else {
itemTouchHelper.attachToRecyclerView(null)
}
when (customField.sortCondition) {
CustomField.Sort.DEFAULT.uniqueIdentifier -> sortDefault.isChecked = true
CustomField.Sort.ASCENDING.uniqueIdentifier -> sortAscending.isChecked = true
CustomField.Sort.DESCENDING.uniqueIdentifier -> sortDescending.isChecked = true
}
addItem.setOnClickListener { addItem.setOnClickListener {
customField.addEntry() val customFieldEntry = customField.addEntry()
updateAdapterUI() rowRepresentableAdapter.notifyDataSetChanged()
onRowSelected(-1, customFieldEntry)
} }
sortChoices.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() {
override fun onCheckedChanged(group: ChipGroup, checkedId: Int) {
super.onCheckedChanged(group, checkedId)
@SuppressWarnings
if (checkedId < 0) { // when unchecked, checkedId returns -1, causing a crash
return
}
when (checkedId) {
R.id.sortDefault -> customField.sortCondition = CustomField.Sort.DEFAULT.uniqueIdentifier
R.id.sortAscending -> customField.sortCondition = CustomField.Sort.ASCENDING.uniqueIdentifier
R.id.sortDescending -> customField.sortCondition = CustomField.Sort.DESCENDING.uniqueIdentifier
}
if (customField.sortCondition == CustomField.Sort.DEFAULT.uniqueIdentifier) {
itemTouchHelper.attachToRecyclerView(recyclerView)
} else {
itemTouchHelper.attachToRecyclerView(null)
}
rowRepresentableAdapter.notifyDataSetChanged()
}
})
updateUI() updateUI()
updateAdapterUI() rowRepresentableAdapter.notifyDataSetChanged()
if (!this.deleteButtonShouldAppear) {
rowRepresentableForPosition(0)?.let {
onRowSelected(0, it)
}
}
val entries = getRealm().where<CustomFieldEntry>().equalTo("customField.id", customField.id).findAll()
Timber.d("delete customField: entries: ${entries.size}")
entries.forEach {
val sessions = getRealm().where<Session>().contains("customFieldEntries.id", it.id).findAll()
Timber.d("Sessions: ${sessions.size} with entry value: ${it.value}")
}
} }
/** /**
* Update UI * Update UI
*/ */
@ -203,27 +290,4 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
} }
} }
/**
* Update adapter UI
*/
private fun updateAdapterUI() {
rowRepresentableAdapter.notifyDataSetChanged()
/*
//TODO: Code to animate changes
// Test:
// not really, i agree with your part about performance but notifyDataSetChanged() does not kill animations,
// to animate using notifyDataSetChanged(), a) call setHasStableIds(true) on your RecyclerView.Adapter object and b)
// override getItemId inside your Adapter to return a unique long value for each row and check it out, the animations do work
customField.adapterRows()?.let {
val diffResult = DiffUtil.calculateDiff(RowRepresentableDiffCallback(it, oldRows))
rowRepresentableAdapter.updateRows(diffResult)
oldRows.clear()
oldRows.addAll(it)
}
*/
}
} }

@ -76,6 +76,7 @@ open class DataManagerFragment : RealmFragment() {
*/ */
protected open fun initData() { protected open fun initData() {
this.deleteButtonShouldAppear = this.primaryKey != null
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey) this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
} }
@ -97,8 +98,8 @@ open class DataManagerFragment : RealmFragment() {
val uniqueIdentifier = (managedItem as Savable).id val uniqueIdentifier = (managedItem as Savable).id
finishActivityWithResult(uniqueIdentifier) finishActivityWithResult(uniqueIdentifier)
} }
} }
onDataSaved()
} }
else -> { else -> {
val message = savable.getFailedSaveMessage(status) val message = savable.getFailedSaveMessage(status)
@ -109,9 +110,10 @@ open class DataManagerFragment : RealmFragment() {
} }
} }
} else -> { }
throw ConfigurationException("Save action called on un-Savable object") else -> {
} throw ConfigurationException("Save action called on un-Savable object")
}
} }
} }
@ -150,5 +152,7 @@ open class DataManagerFragment : RealmFragment() {
activity?.finish() activity?.finish()
} }
open fun onDataSaved() {}
} }

@ -105,7 +105,6 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
onRowSelected(0, it) onRowSelected(0, it)
} }
} }
} }
} }

@ -37,6 +37,12 @@ interface ImageDecorator {
get() { get() {
return null return null
} }
val imageTint: Int?
get() {
return null
}
} }
/** /**

@ -8,6 +8,7 @@ import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -64,6 +65,7 @@ enum class RowViewType(private var layoutRes: Int) {
TITLE_ICON_ARROW(R.layout.row_title_icon_arrow), TITLE_ICON_ARROW(R.layout.row_title_icon_arrow),
TITLE_VALUE(R.layout.row_title_value), TITLE_VALUE(R.layout.row_title_value),
TITLE_VALUE_ARROW(R.layout.row_title_value_arrow), TITLE_VALUE_ARROW(R.layout.row_title_value_arrow),
TITLE_VALUE_ACTION(R.layout.row_title_value_action),
TITLE_SWITCH(R.layout.row_title_switch), TITLE_SWITCH(R.layout.row_title_switch),
TITLE_GRID(R.layout.row_bottom_sheet_grid_title), TITLE_GRID(R.layout.row_bottom_sheet_grid_title),
DATA(R.layout.row_title), DATA(R.layout.row_title),
@ -97,7 +99,7 @@ enum class RowViewType(private var layoutRes: Int) {
// Row View Holder // Row View Holder
HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG, LOCATION_TITLE, HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG, LOCATION_TITLE,
INFO, TITLE, TITLE_ARROW, TITLE_ICON_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_GRID, INFO, TITLE, TITLE_ARROW, TITLE_ICON_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_VALUE_ACTION, TITLE_GRID,
TITLE_SWITCH, TITLE_CHECK, TITLE_VALUE_CHECK, TITLE_SWITCH, TITLE_CHECK, TITLE_VALUE_CHECK,
DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(layout) DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(layout)
@ -186,11 +188,24 @@ enum class RowViewType(private var layoutRes: Int) {
// Icon // Icon
itemView.findViewById<AppCompatImageView?>(R.id.icon)?.let { imageView -> itemView.findViewById<AppCompatImageView?>(R.id.icon)?.let { imageView ->
imageView.setImageDrawable(null)
row.imageRes?.let { imageRes -> row.imageRes?.let { imageRes ->
imageView.setImageResource(imageRes) imageView.setImageResource(imageRes)
} }
} }
// Action
itemView.findViewById<AppCompatImageView>(R.id.action)?.let { imageView ->
imageView.setImageDrawable(null)
row.imageRes?.let { imageRes ->
imageView.visibility = View.VISIBLE
imageView.setImageResource(imageRes)
}
row.imageTint?.let { color ->
imageView.setColorFilter(ContextCompat.getColor(imageView.context, color))
}
}
// Listener // Listener
val listener = View.OnClickListener { val listener = View.OnClickListener {
itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let { itemView.findViewById<SwitchCompat?>(R.id.switchView)?.let {

@ -16,7 +16,7 @@ enum class FilterCategoryRow(override val resId: Int?, override val viewType: In
GENERAL(R.string.general), GENERAL(R.string.general),
DATE(R.string.date), DATE(R.string.date),
TIME_FRAME(R.string.duration), TIME_FRAME(R.string.duration),
SESSIONS(R.string.sessions), //SESSIONS(R.string.sessions),
CASH(R.string.cash), CASH(R.string.cash),
TOURNAMENT(R.string.tournament), TOURNAMENT(R.string.tournament),
ONLINE(R.string.online), ONLINE(R.string.online),
@ -92,7 +92,7 @@ enum class FilterCategoryRow(override val resId: Int?, override val viewType: In
SESSION_DURATION, SESSION_DURATION,
TIME_FRAME_RANGE TIME_FRAME_RANGE
) )
SESSIONS -> arrayListOf(FilterSectionRow.SESSIONS) //SESSIONS -> arrayListOf(FilterSectionRow.SESSIONS)
BANKROLLS -> arrayListOf( BANKROLLS -> arrayListOf(
BANKROLL BANKROLL
) )

@ -87,7 +87,7 @@ enum class FilterSectionRow(override val resId: Int?) : RowRepresentable {
TIME_FRAME_RANGE -> arrayListOf(QueryCondition.StartedFromTime(), QueryCondition.EndedToTime()) TIME_FRAME_RANGE -> arrayListOf(QueryCondition.StartedFromTime(), QueryCondition.EndedToTime())
// Sessions // Sessions
SESSIONS -> arrayListOf(QueryCondition.LastGame(), QueryCondition.LastSession()) //SESSIONS -> arrayListOf(QueryCondition.LastGame(), QueryCondition.LastSession())
// Cash // Cash
BLIND -> Criteria.Blinds.queryConditions.mapFirstCondition() BLIND -> Criteria.Blinds.queryConditions.mapFirstCondition()
@ -113,9 +113,7 @@ enum class FilterSectionRow(override val resId: Int?) : RowRepresentable {
addAll(QueryCondition.moreOrLess<QueryCondition.NetAmountWon>()) addAll(QueryCondition.moreOrLess<QueryCondition.NetAmountWon>())
addAll(QueryCondition.moreOrLess<QueryCondition.NetAmountLost>()) addAll(QueryCondition.moreOrLess<QueryCondition.NetAmountLost>())
} }
TRANSACTION_TYPES -> arrayListOf<QueryCondition>().apply { TRANSACTION_TYPES -> Criteria.TransactionTypes.queryConditions.mapFirstCondition()
}
else -> arrayListOf() else -> arrayListOf()
}.apply { }.apply {
this.forEach { this.forEach {

@ -38,9 +38,7 @@ fun Double.round(): String {
} }
fun Double.roundOffDecimal(): Double { fun Double.roundOffDecimal(): Double {
val df = DecimalFormat("#.##") return this.toBigDecimal().setScale(2, RoundingMode.CEILING).toDouble()
df.roundingMode = RoundingMode.CEILING
return df.format(this).toDouble()
} }
fun Double.formatted(): String { fun Double.formatted(): String {

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z"/>
</vector>

@ -65,6 +65,7 @@
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:visibility="gone" android:visibility="gone"
app:contentInsetStart="8dp" app:contentInsetStart="8dp"
app:contentInsetEnd="8dp"
tools:visibility="visible"> tools:visibility="visible">
<FrameLayout <FrameLayout
@ -76,10 +77,40 @@
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:padding="4dp" android:padding="4dp"
android:layout_gravity="end"
android:background="?selectableItemBackgroundBorderless" android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_add" android:src="@drawable/ic_add"
android:tint="@color/green" /> android:tint="@color/green" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/sortChoices"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipSpacing="8dp"
android:layout_gravity="center"
app:singleSelection="true">
<com.google.android.material.chip.Chip
android:id="@+id/sortDefault"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text=" - " />
<com.google.android.material.chip.Chip
android:id="@+id/sortAscending"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A..Z" />
<com.google.android.material.chip.Chip
android:id="@+id/sortDescending"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Z..A" />
</com.google.android.material.chip.ChipGroup>
</FrameLayout> </FrameLayout>
</com.google.android.material.bottomappbar.BottomAppBar> </com.google.android.material.bottomappbar.BottomAppBar>

@ -1,52 +1,53 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content" android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="280dp" android:layout_width="match_parent"
android:layout_gravity="center" android:layout_height="wrap_content"
android:layout_height="wrap_content"> android:layout_gravity="center">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title" android:id="@+id/title"
android:gravity="center" style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeatureTitle"
app:layout_constraintTop_toTopOf="parent" android:layout_width="match_parent"
app:layout_constraintStart_toStartOf="parent" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:gravity="center"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeatureTitle" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="match_parent" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"
tools:text="Title"/> tools:text="Title" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon" android:id="@+id/icon"
android:tint="@color/green" android:layout_width="48dp"
app:layout_constraintTop_toBottomOf="@id/title" android:layout_height="48dp"
app:layout_constraintStart_toStartOf="parent" android:src="@drawable/ic_baseline_all_inclusive"
app:layout_constraintEnd_toEndOf="parent" android:tint="@color/green"
android:src="@drawable/ic_baseline_all_inclusive_24px" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="48dp" app:layout_constraintStart_toStartOf="parent"
android:layout_height="48dp" /> app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:paddingLeft="24dp" android:id="@+id/description"
android:paddingRight="24dp" style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature"
android:id="@+id/description" android:layout_width="0dp"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="20dp"
android:gravity="center" android:gravity="center"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature" android:maxLines="10"
app:layout_constraintTop_toBottomOf="@id/icon" android:paddingLeft="24dp"
app:layout_constraintStart_toStartOf="parent" android:paddingRight="24dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent"
android:maxLines="5" app:layout_constraintTop_toBottomOf="@id/icon"
tools:text="Description qui va avec le text youpi. Il peut y avoir plusieurs ligne de texte oui tout a fait."/> app:layout_constraintWidth_percent="0.8"
tools:text="Description qui va avec le text youpi. Il peut y avoir plusieurs ligne de texte oui tout a fait." />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>

@ -39,23 +39,29 @@
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="200dp" android:layout_width="0dp"
android:layout_marginTop="100dp" android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/freetrial" android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toTopOf="@+id/price"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" /> app:layout_constraintHeight_percent="0.6"
app:layout_constraintVertical_bias="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/freetrial" />
<LinearLayout <LinearLayout
android:orientation="horizontal"
android:id="@+id/pageIndicator" android:id="@+id/pageIndicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/pager" android:layout_marginBottom="8dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/price"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintVertical_bias="0.6"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintTop_toBottomOf="@id/pager" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/price" android:id="@+id/price"

@ -13,7 +13,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:textSize="18sp" android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guidelineStart" app:layout_constraintStart_toStartOf="@+id/guidelineStart"
@ -40,7 +40,6 @@
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:background="?selectableItemBackgroundBorderless"
android:padding="4dp" android:padding="4dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

@ -2,11 +2,18 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<item
android:id="@+id/actionDelete"
android:icon="@drawable/ic_outline_delete"
android:orderInCategory="0"
android:title="@string/delete"
android:visible="false"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/actionClear" android:id="@+id/actionClear"
android:icon="@drawable/ic_baseline_not_interested"
android:orderInCategory="100" android:orderInCategory="100"
android:title="@string/clear" android:title="@string/clear"
android:icon="@drawable/ic_baseline_not_interested"
app:showAsAction="always" /> app:showAsAction="always" />
<item <item
android:id="@+id/actionAdd" android:id="@+id/actionAdd"
@ -20,4 +27,5 @@
android:orderInCategory="300" android:orderInCategory="300"
android:title="@string/done" android:title="@string/done"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
</menu> </menu>
Loading…
Cancel
Save