Fixes compilation issue + use latest refactored methods

dev
Laurent 7 years ago
commit 4fbc01af05
  1. 7
      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. 13
      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. 75
      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. 6
      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. 136
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  20. 8
      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. 45
      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 io.realm.RealmQuery
import io.realm.kotlin.where
import net.pokeranalytics.android.R
fun List<Query>.mapFirstCondition() : List<QueryCondition> {
@ -67,6 +68,12 @@ class Query {
}
}
val queryLast = this.conditions.filter {
it is QueryCondition.Last
}.firstOrNull()
queryLast?.let {
return realmQuery.limit((it as QueryCondition.Last).singleValue.toLong())
}
return realmQuery
}

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

@ -121,4 +121,9 @@ interface Deletable : Identifiable {
*/
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.RealmMigration
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.CustomFieldEntry
import timber.log.Timber
@ -104,15 +105,16 @@ class PokerAnalyticsMigration : RealmMigration {
}
schema.create("CustomFieldEntry")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addField("value", String::class.java).setNullable("type", false)
it.addField("order", Integer::class.java).setNullable("type", false)
it.addField("value", String::class.java).setNullable("value", false)
it.addField("order", Integer::class.java).setNullable("order", false)
it.addField("customField", CustomField::class.java).setNullable("customField", false)
}
schema.get("CustomField")?.let {
it.addField("type", Integer::class.java).setNullable("type", false)
it.addField("duplicateValue", Boolean::class.java)
it.addField("sortCondition", Integer::class.java)
it.addRealmListField("entries", CustomFieldEntry::class.java)
}
schema.get("ReportSetup")?.let {
it.addRealmListField("statIds", Int::class.java)
it.addRealmListField("criteriaIds", Int::class.java)
@ -121,11 +123,14 @@ class PokerAnalyticsMigration : RealmMigration {
it.addRealmObjectField("filter", filterSchema)
}
}
schema.get("Session")?.let {
it.addField("startDateHourMinuteComponent", Double::class.java).setNullable("startDateHourMinuteComponent", true)
it.addField("endDateHourMinuteComponent", Double::class.java).setNullable("endDateHourMinuteComponent", true)
it.addRealmListField("customFieldEntries", CustomFieldEntry::class.java)
}
schema.get("Filter")?.addField("filterableTypeOrdinal", Integer::class.java)
currentVersion++
}
}

@ -1,33 +1,42 @@
package net.pokeranalytics.android.model.realm
import android.content.Context
import android.text.InputType
import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
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.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import timber.log.Timber
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import java.util.*
import kotlin.collections.ArrayList
open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {
enum class Type(var resId: Int) {
LIST(R.string.enum_custom_field_type),
NUMBER(R.string.number),
AMOUNT(R.string.amount)
enum class Type(override var uniqueIdentifier: Int, var resId: Int) : IntIdentifiable {
LIST(0, R.string.enum_custom_field_type),
NUMBER(1, R.string.number),
AMOUNT(2, R.string.amount)
}
enum class Sort(override var uniqueIdentifier: Int) : IntIdentifiable {
DEFAULT(0),
ASCENDING(1),
DESCENDING(2)
}
@PrimaryKey
@ -37,7 +46,7 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
override var name: String = ""
// Migration
var type: Int = Type.LIST.ordinal
var type: Int = Type.LIST.uniqueIdentifier
set(value) {
field = value
this.updateRowRepresentation()
@ -45,20 +54,32 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
var duplicateValue: Boolean = false
var entries: RealmList<CustomFieldEntry> = RealmList()
var sortCondition: Int = Sort.DEFAULT.uniqueIdentifier
set(value) {
field = value
sortEntries()
updateRowRepresentation()
}
// @todo
override fun getDisplayName(context: Context): String {
return this.name
}
@Ignore
private var entriesToDelete: ArrayList<CustomFieldEntry> = ArrayList()
@Ignore
override val viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal
override var viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal
@Ignore
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>? {
return rowRepresentation
}
@ -72,20 +93,21 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
}
override fun isValidForSave(): Boolean {
return true
}
override fun alreadyExists(realm: Realm): Boolean {
return realm.where(this::class.java).equalTo("name", this.name).and().notEqualTo("id", this.id).findAll().isNotEmpty()
return super.isValidForSave()
}
override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
return when (status) {
SaveValidityStatus.DATA_INVALID -> R.string.cf_empty_field_error
SaveValidityStatus.ALREADY_EXISTS -> R.string.duplicate_cf_error
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 {
return true
}
@ -95,6 +117,21 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
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>? {
return when (row) {
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> {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.add(CustomFieldRow.TYPE)
if (type == Type.LIST.ordinal && entries.size >= 0) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.items_list))
if (entries.isNotEmpty()) {
Timber.d("entries: ${entries.size}")
entries.sortBy { it.order }
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.items_list))
sortEntries()
entries.forEach { customFieldEntry ->
customFieldEntry.isMovable = sortCondition == Sort.DEFAULT.uniqueIdentifier
}
rows.addAll(entries)
}
}
Timber.d("rows: ${rows.size}")
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() {
this.rowRepresentation = this.updatedRowRepresentationForCurrentState()
}
/**
* Add entry
* Add an entry
*/
fun addEntry() {
fun addEntry(): CustomFieldEntry {
val entry = CustomFieldEntry()
entry.order = (entries.lastOrNull()?.order ?: 0) + 1
entry.customField = this
entries.add(entry)
sortEntries()
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.text.InputType
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
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.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.toCurrency
import java.util.*
import java.util.Currency
open class CustomFieldEntry : RealmObject(), RowRepresentable {
open class CustomFieldEntry : RealmObject(), Manageable, RowRepresentable {
@PrimaryKey
var id = UUID.randomUUID().toString()
var value: String = ""
override var id = UUID.randomUUID().toString()
var order: Int = 0
var customField: CustomField? = null
var value: String = ""
@Ignore
override val viewType: Int = RowViewType.TITLE.ordinal
var isMovable: Boolean = false
@Ignore
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
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.QueryCondition
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.FilterCategoryRow
import java.util.*
@ -22,8 +23,9 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable {
companion object {
// Create a new instance
fun newInstance(realm: Realm): Filter {
fun newInstance(realm: Realm, filterableType:Int): Filter {
val filter = Filter()
filter.filterableTypeOrdinal = filterableType
return realm.copyToRealm(filter)
}
@ -60,6 +62,16 @@ open class Filter : RealmObject(), RowRepresentable, Identifiable {
var filterConditions: RealmList<FilterCondition> = RealmList()
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>) {
println("list of querys saving: ${filterConditionRows.map { it.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"
AnyTournamentType::class.java -> "tournamentType"
AnyBlind::class.java -> "blinds"
NumberOfTable::class.java -> "numberOfTable"
NumberOfTable::class.java -> "numberOfTables"
NetAmountWon::class.java, NetAmountLost::class.java -> "computableResults.ratedNet"
NumberOfRebuy::class.java -> "result.numberOfRebuy"
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"
AnyMonthOfYear::class.java -> "month"
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"
EndedToTime::class.java -> "endDateHourMinuteComponent"
Duration::class.java -> "netDuration"
@ -293,6 +293,9 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// The features of the tournament, like Knockout, Shootout, Turbo...
var tournamentFeatures: RealmList<TournamentFeature> = RealmList()
// The custom fields values
var customFieldEntries: RealmList<CustomFieldEntry> = RealmList()
fun bankrollHasBeenUpdated() {
formatBlinds()
}
@ -589,11 +592,13 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
* Delete the object from realm
*/
fun delete() {
if (isValid) {
realm.executeTransaction {
cleanup()
deleteFromRealm()
}
}
}
/**
* This method is called whenever a session is about to be deleted
@ -613,6 +618,16 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
@Ignore
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 {
return "Session ${this.creationDate}"
}
@ -678,6 +693,13 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
// Rows
rows.addAll(SessionRow.getRows(this))
// Add custom fields
realm?.let {
rows.add(SeparatorRow())
rows.addAll(it.sorted<CustomField>())
}
return rows
}
@ -711,11 +733,13 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
SessionRow.START_DATE -> this.startDate?.shortDateTime() ?: NULL_TEXT
SessionRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT
SessionRow.TIPS -> result?.tips?.toCurrency(currency) ?: NULL_TEXT
SessionRow.TOURNAMENT_TYPE -> this.tournamentType?.let {
SessionRow.TOURNAMENT_TYPE -> {
this.tournamentType?.let {
TournamentType.values()[it].localizedTitle(context)
} ?: run {
NULL_TEXT
}
}
SessionRow.TOURNAMENT_FEATURE -> {
if (tournamentFeatures.size > 2) {
"${tournamentFeatures.subList(0, 2).joinToString {
@ -730,6 +754,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
}
}
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}")
}
}
@ -837,6 +867,19 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
"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
}
}
@ -946,7 +989,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
result = localResult
}
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 -> {
value?.let {
@ -956,6 +999,28 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
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,
groupName: String,
context: Context
) : LegendContent {
): LegendContent {
when (style) {
GraphFragment.Style.MULTILINE -> {

@ -37,8 +37,12 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return when (queryCondition) {
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.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
}
}

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

@ -24,10 +24,6 @@ import net.pokeranalytics.android.util.billing.AppGuard
import java.io.File
// Sizes
val Int.dp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@ -70,8 +66,9 @@ fun PokerAnalyticsActivity.openPlayStorePage() {
}
// Open email for "Contact us"
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()}"
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 emailIntent = Intent(Intent.ACTION_SEND)
@ -105,6 +102,7 @@ fun PokerAnalyticsActivity.openUrl(url: String) {
fun PokerAnalyticsActivity.showAlertDialog(title: Int? = null, message: Int? = null) {
showAlertDialog(this, title, message)
}
fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = null) {
context?.let {
showAlertDialog(it, title, message)
@ -114,7 +112,10 @@ fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = n
/**
* 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)
title?.let {
builder.setTitle(title)
@ -122,7 +123,14 @@ fun showAlertDialog(context: Context, title: Int? = null, message: Int? = null)
message?.let {
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()
}

@ -137,8 +137,14 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate, FilterHandler {
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) {
0 -> recyclerView.adapter = feedSessionAdapter
1 -> recyclerView.adapter = feedTransactionAdapter
0 -> {
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")
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
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.feedSessionAdapter.refreshData()
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 distinctDateSessions = filter?.results("year", "month") ?: run { getRealm().where<Session>().distinct("year", "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 = sessionFilter?.results("year", "month") ?: run { getRealm().where<Session>().distinct("year", "month").findAll() }.sort("startDate", Sort.DESCENDING)
this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(this, realmSessions, pendingSessions, distinctDateSessions)
// 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.feedTransactionAdapter.refreshData()
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)
val viewManager = SmoothScrollLinearLayoutManager(requireContext())

@ -158,7 +158,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
isUpdating = true
} ?: run {
realm.beginTransaction()
currentFilter = Filter.newInstance(realm)
currentFilter = Filter.newInstance(realm, this.filterableType.ordinal)
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.IAPProducts
import net.pokeranalytics.android.util.billing.PurchaseDelegate
import java.lang.ref.WeakReference
import java.time.Period
class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListener, PurchaseDelegate, ViewPager.OnPageChangeListener {
companion object {
val parallax: Float = 64f.px
}
private var pagerAdapter: ScreenSlidePagerAdapter? = null
private var selectedProduct: SkuDetails? = null
override fun onCreate(savedInstanceState: Bundle?) {
@ -85,7 +91,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
// Pager
// 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.addOnPageChangeListener(this)
@ -108,7 +114,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
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 var fragments: HashMap<Int, WeakReference<ScreenSlidePageFragment>> = HashMap()
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(
FeatureDescriptor(R.drawable.ic_baseline_all_inclusive_24px, 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_vpn_key_24px, 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_all_inclusive, R.string.f_unlimited, R.string.f_unlimited_desc),
FeatureDescriptor(R.drawable.ic_baseline_wifi_off, R.string.f_offline, R.string.f_offline_desc),
FeatureDescriptor(R.drawable.ic_baseline_vpn_key, R.string.f_privacy, R.string.f_privacy_desc),
FeatureDescriptor(R.drawable.ic_baseline_email, R.string.f_support, R.string.f_support_desc)
)
override fun getCount(): Int = this.dataSource.size
@ -133,21 +147,27 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
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
override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) {
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) {
this.hideLoader()
selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier }
updateUI()
}
}
private fun updateUI() {
this.selectedProduct?.let {
this.purchase.isEnabled = true
val perYearString = requireContext().getString(R.string.year_subscription)
@ -162,9 +182,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
val formattedFreeTrial =
"$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial)
this.freetrial.text = formattedFreeTrial
}
}
// PurchaseDelegate
@ -182,22 +200,30 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
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) {
this.changeColorOfIndicator(position)
updatePagerIndicators(position)
}
private fun changeColorOfIndicator(position: Int) {
private fun updatePagerIndicators(position: Int) {
this.pageIndicator.children.forEachIndexed { index, view ->
val drawable = view.background
when (drawable) {
is GradientDrawable -> {
val color = if (position == index) R.color.white else R.color.quantum_grey
drawable.setColor(requireContext().getColor(color))
} else -> {}
}
else -> {
}
}
}
}

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

@ -22,6 +22,15 @@ class ScreenSlidePageFragment(var iconResId: Int, var titleResId: Int, var descr
this.icon.setImageResource(this.iconResId)
this.title.text = requireContext().getString(this.titleResId)
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
private var isClearable: Boolean = true
private var isDeletable: Boolean = false
private var rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>? = null
companion object {
@ -44,7 +45,8 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
delegate: RowRepresentableDelegate,
rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>?,
isClearable: Boolean? = true,
currentCurrency: Currency? = null
currentCurrency: Currency? = null,
isDeletable: Boolean? = false
): BottomSheetFragment {
val bottomSheetFragment = row.bottomSheetType.newInstance()
bottomSheetFragment.show(fragmentManager, "bottomSheet")
@ -52,6 +54,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
bottomSheetFragment.delegate = delegate
bottomSheetFragment.rowRepresentableEditDescriptors = rowRepresentableEditDescriptors
bottomSheetFragment.isClearable = isClearable ?: true
bottomSheetFragment.isDeletable = isDeletable ?: true
bottomSheetFragment.currentCurrency = currentCurrency
return bottomSheetFragment
}
@ -111,6 +114,13 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
dismiss()
true
}
bottomSheetToolbar.menu.findItem(R.id.actionDelete).setOnMenuItemClickListener {
delegate.onRowDeleted(row)
dismiss()
true
}
bottomSheetToolbar.menu.findItem(R.id.actionAdd).setOnMenuItemClickListener {
val liveData = when (row) {
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.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 net.pokeranalytics.android.R
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.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -36,7 +35,7 @@ class BottomSheetStaticListFragment : BottomSheetFragment(), StaticRowRepresenta
}
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()
super.onRowSelected(position, row, fromAction)
}

@ -7,13 +7,18 @@ import android.view.ViewGroup
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.ItemTouchHelper
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 net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.CustomField
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.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
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.view.RowRepresentable
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 timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
/**
* Custom EditableDataFragment to manage the Transaction data
@ -34,8 +40,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
return this.item as CustomField
}
private val oldRows: ArrayList<RowRepresentable> = ArrayList()
private val deletedCustomFieldEntries: ArrayList<CustomFieldEntry> = ArrayList()
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 {
if (target.adapterPosition <= 2) {
return false
}
@ -71,7 +77,15 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
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)
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?) {
super.onViewCreated(view, savedInstanceState)
//shouldOpenKeyboard = false
initUI()
}
@ -146,27 +159,50 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
when (row) {
is CustomFieldEntry -> {
val data = customField.editDescriptors(row)
Timber.d("data: $data")
BottomSheetFragment.create(fragmentManager, row, this, data, false)
BottomSheetFragment.create(fragmentManager, row, this, data, isClearable = false, isDeletable = true)
}
else -> super.onRowSelected(position, row, fromAction)
}
}
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
Timber.d("onRowValueChanged: $row => $value")
when (row) {
is CustomFieldEntry -> {
row.value = value as String? ?: ""
rowRepresentableAdapter.refreshRow(row)
row.updateValue(value, row)
customField.updateRowRepresentation()
rowRepresentableAdapter.notifyDataSetChanged()
}
CustomFieldRow.TYPE -> {
customField.updateValue(value, row)
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.visibility = View.VISIBLE
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 {
customField.addEntry()
updateAdapterUI()
val customFieldEntry = customField.addEntry()
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()
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
*/
@ -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() {
this.deleteButtonShouldAppear = this.primaryKey != null
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
}
@ -97,8 +98,8 @@ open class DataManagerFragment : RealmFragment() {
val uniqueIdentifier = (managedItem as Savable).id
finishActivityWithResult(uniqueIdentifier)
}
}
onDataSaved()
}
else -> {
val message = savable.getFailedSaveMessage(status)
@ -109,7 +110,8 @@ open class DataManagerFragment : RealmFragment() {
}
}
} else -> {
}
else -> {
throw ConfigurationException("Save action called on un-Savable object")
}
}
@ -150,5 +152,7 @@ open class DataManagerFragment : RealmFragment() {
activity?.finish()
}
open fun onDataSaved() {}
}

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

@ -37,6 +37,12 @@ interface ImageDecorator {
get() {
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.SwitchCompat
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
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_VALUE(R.layout.row_title_value),
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_GRID(R.layout.row_bottom_sheet_grid_title),
DATA(R.layout.row_title),
@ -97,7 +99,7 @@ enum class RowViewType(private var layoutRes: Int) {
// Row View Holder
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,
DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder(layout)
@ -186,11 +188,24 @@ enum class RowViewType(private var layoutRes: Int) {
// Icon
itemView.findViewById<AppCompatImageView?>(R.id.icon)?.let { imageView ->
imageView.setImageDrawable(null)
row.imageRes?.let { 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
val listener = View.OnClickListener {
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),
DATE(R.string.date),
TIME_FRAME(R.string.duration),
SESSIONS(R.string.sessions),
//SESSIONS(R.string.sessions),
CASH(R.string.cash),
TOURNAMENT(R.string.tournament),
ONLINE(R.string.online),
@ -92,7 +92,7 @@ enum class FilterCategoryRow(override val resId: Int?, override val viewType: In
SESSION_DURATION,
TIME_FRAME_RANGE
)
SESSIONS -> arrayListOf(FilterSectionRow.SESSIONS)
//SESSIONS -> arrayListOf(FilterSectionRow.SESSIONS)
BANKROLLS -> arrayListOf(
BANKROLL
)

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

@ -38,9 +38,7 @@ fun Double.round(): String {
}
fun Double.roundOffDecimal(): Double {
val df = DecimalFormat("#.##")
df.roundingMode = RoundingMode.CEILING
return df.format(this).toDouble()
return this.toBigDecimal().setScale(2, RoundingMode.CEILING).toDouble()
}
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:visibility="gone"
app:contentInsetStart="8dp"
app:contentInsetEnd="8dp"
tools:visibility="visible">
<FrameLayout
@ -76,10 +77,40 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="4dp"
android:layout_gravity="end"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_add"
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>
</com.google.android.material.bottomappbar.BottomAppBar>

@ -7,45 +7,46 @@
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="280dp"
android:layout_gravity="center"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeatureTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Title"/>
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_baseline_all_inclusive"
android:tint="@color/green"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_baseline_all_inclusive_24px"
android:layout_width="48dp"
android:layout_height="48dp" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:id="@+id/description"
android:layout_width="match_parent"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature"
app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
android:maxLines="10"
android:paddingLeft="24dp"
android:paddingRight="24dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp"
android:maxLines="5"
tools:text="Description qui va avec le text youpi. Il peut y avoir plusieurs ligne de texte oui tout a fait."/>
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/icon"
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>

@ -39,23 +39,29 @@
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="100dp"
app:layout_constraintTop_toBottomOf="@id/freetrial"
app:layout_constraintStart_toStartOf="parent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/price"
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
android:orientation="horizontal"
android:id="@+id/pageIndicator"
android:layout_width="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_constraintVertical_bias="0.6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintTop_toBottomOf="@id/pager" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/price"

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

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