Merge branch 'dev' of gitlab.com:stax-river/poker-analytics into dev

dev
Laurent 6 years ago
commit b1fce6a99d
  1. 17
      app/build.gradle
  2. 1
      app/proguard-rules.pro
  3. 8
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/BankrollInstrumentedUnitTest.kt
  4. 52
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/CustomFieldFilterInstrumentedUnitTest.kt
  5. 12
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/filter/TransactionFilterInstrumentedUnitTest.kt
  6. 34
      app/src/main/AndroidManifest.xml
  7. 6
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  8. 5
      app/src/main/java/net/pokeranalytics/android/calculus/Calculator.kt
  9. 34
      app/src/main/java/net/pokeranalytics/android/calculus/ComputedStat.kt
  10. 16
      app/src/main/java/net/pokeranalytics/android/calculus/Report.kt
  11. 63
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  12. 13
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt
  13. 117
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt
  14. 118
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReportManager.kt
  15. 3
      app/src/main/java/net/pokeranalytics/android/exceptions/Exceptions.kt
  16. 3
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  17. 44
      app/src/main/java/net/pokeranalytics/android/model/extensions/SessionExtensions.kt
  18. 3
      app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt
  19. 101
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt
  20. 4
      app/src/main/java/net/pokeranalytics/android/model/interfaces/CountableUsage.kt
  21. 9
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Dated.kt
  22. 13
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  23. 7
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Timed.kt
  24. 29
      app/src/main/java/net/pokeranalytics/android/model/migrations/Patcher.kt
  25. 7
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  26. 28
      app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt
  27. 6
      app/src/main/java/net/pokeranalytics/android/model/realm/ComputableResult.kt
  28. 12
      app/src/main/java/net/pokeranalytics/android/model/realm/Currency.kt
  29. 101
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  30. 33
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  31. 219
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  32. 27
      app/src/main/java/net/pokeranalytics/android/model/realm/Game.kt
  33. 5
      app/src/main/java/net/pokeranalytics/android/model/realm/Location.kt
  34. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  35. 5
      app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt
  36. 176
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  37. 11
      app/src/main/java/net/pokeranalytics/android/model/realm/SessionSet.kt
  38. 2
      app/src/main/java/net/pokeranalytics/android/model/realm/TimeFrame.kt
  39. 23
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentFeature.kt
  40. 18
      app/src/main/java/net/pokeranalytics/android/model/realm/TournamentName.kt
  41. 26
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  42. 65
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  43. 39
      app/src/main/java/net/pokeranalytics/android/model/utils/DataUtils.kt
  44. 22
      app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt
  45. 31
      app/src/main/java/net/pokeranalytics/android/model/utils/Seed.kt
  46. 7
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt
  47. 21
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionUtils.kt
  48. 48
      app/src/main/java/net/pokeranalytics/android/ui/activity/BankrollActivity.kt
  49. 38
      app/src/main/java/net/pokeranalytics/android/ui/activity/BillingActivity.kt
  50. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/EditableDataActivity.kt
  51. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/FiltersActivity.kt
  52. 58
      app/src/main/java/net/pokeranalytics/android/ui/activity/FiltersListActivity.kt
  53. 90
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  54. 88
      app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt
  55. 27
      app/src/main/java/net/pokeranalytics/android/ui/activity/NewDataMenuActivity.kt
  56. 27
      app/src/main/java/net/pokeranalytics/android/ui/activity/SessionActivity.kt
  57. 33
      app/src/main/java/net/pokeranalytics/android/ui/activity/Top10Activity.kt
  58. 9
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/Codes.kt
  59. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/PokerAnalyticsActivity.kt
  60. 136
      app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedSessionRowRepresentableAdapter.kt
  61. 10
      app/src/main/java/net/pokeranalytics/android/ui/adapter/FeedTransactionRowRepresentableAdapter.kt
  62. 4
      app/src/main/java/net/pokeranalytics/android/ui/adapter/HomePagerAdapter.kt
  63. 9
      app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableAdapter.kt
  64. 22
      app/src/main/java/net/pokeranalytics/android/ui/adapter/RowRepresentableDataSource.kt
  65. 163
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollDetailsFragment.kt
  66. 160
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
  67. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CalendarFragment.kt
  68. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CurrenciesFragment.kt
  69. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  70. 230
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt
  71. 42
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FilterDetailsFragment.kt
  72. 52
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersFragment.kt
  73. 112
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FiltersListFragment.kt
  74. 23
      app/src/main/java/net/pokeranalytics/android/ui/fragment/GraphFragment.kt
  75. 77
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt
  76. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/MoreFragment.kt
  77. 79
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  78. 50
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  79. 5
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  80. 34
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt
  81. 175
      app/src/main/java/net/pokeranalytics/android/ui/fragment/Top10Fragment.kt
  82. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  83. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt
  84. 48
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetType.kt
  85. 30
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputDoubleEditTextFragment.kt
  86. 24
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputEditTextFragment.kt
  87. 19
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputEditTextMultiLinesFragment.kt
  88. 74
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputFragment.kt
  89. 50
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputFragmentType.kt
  90. 22
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputListFragment.kt
  91. 17
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputListGameFragment.kt
  92. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputMultiSelectionFragment.kt
  93. 26
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputNumericTextFragment.kt
  94. 17
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputStaticListFragment.kt
  95. 19
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputSumFragment.kt
  96. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/input/InputTableSizeGridFragment.kt
  97. 39
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/BankrollDataFragment.kt
  98. 27
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  99. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  100. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -29,8 +29,8 @@ android {
applicationId "net.pokeranalytics.android" applicationId "net.pokeranalytics.android"
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 28 targetSdkVersion 28
versionCode 30 versionCode 50
versionName "2.0" versionName "2.1.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@ -39,6 +39,7 @@ android {
ext.enableCrashlytics = false ext.enableCrashlytics = false
} }
release { release {
useProguard false
minifyEnabled true minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationVariants.all { variant -> applicationVariants.all { variant ->
@ -78,7 +79,7 @@ dependencies {
// Android // Android
implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.2.0-alpha01' implementation 'androidx.core:core-ktx:1.2.0-alpha02'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
@ -99,7 +100,7 @@ dependencies {
implementation 'com.android.billingclient:billing:1.2.2' implementation 'com.android.billingclient:billing:1.2.2'
// Firebase // Firebase
implementation 'com.google.firebase:firebase-core:16.0.9' implementation 'com.google.firebase:firebase-core:17.0.0'
// Crashlytics // Crashlytics
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
@ -114,10 +115,10 @@ dependencies {
implementation 'org.apache.commons:commons-csv:1.6' implementation 'org.apache.commons:commons-csv:1.6'
// Instrumented Tests // Instrumented Tests
androidTestImplementation 'androidx.test:core:1.1.0' androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.1.1' androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
// Test // Test
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

@ -29,6 +29,7 @@
-dontwarn javax.** -dontwarn javax.**
-dontwarn io.realm.** -dontwarn io.realm.**
-keep class net.pokeranalytics.android.model.** { *; } -keep class net.pokeranalytics.android.model.** { *; }
-keep class net.pokeranalytics.android.ui.fragment.** { *; }
# Retrofit # Retrofit

@ -19,7 +19,7 @@ class BankrollInstrumentedUnitTest : SessionInstrumentedUnitTest() {
TransactionType.Value.values().forEachIndexed { index, value -> TransactionType.Value.values().forEachIndexed { index, value ->
val type = TransactionType() val type = TransactionType()
type.additive = value.additive type.additive = value.additive
type.kind = index type.kind = value.uniqueIdentifier
type.lock = true type.lock = true
realm.insertOrUpdate(type) realm.insertOrUpdate(type)
} }
@ -72,12 +72,12 @@ class BankrollInstrumentedUnitTest : SessionInstrumentedUnitTest() {
} }
val br1 = realm.where(Bankroll::class.java).equalTo("name", "br1").findFirst() val br1 = realm.where(Bankroll::class.java).equalTo("name", "br1").findFirst()
val brSetup1 = BankrollReportSetup(br1) val brSetup1 = BankrollReportSetup(br1?.id)
val report1 = BankrollCalculator.computeReport(realm, brSetup1) val report1 = BankrollCalculator.computeReport(realm, brSetup1)
Assert.assertEquals(400.0, report1.total, EPSILON) Assert.assertEquals(400.0, report1.total, EPSILON)
val br2 = realm.where(Bankroll::class.java).equalTo("name", "br2").findFirst() val br2 = realm.where(Bankroll::class.java).equalTo("name", "br2").findFirst()
val brSetup2 = BankrollReportSetup(br2) val brSetup2 = BankrollReportSetup(br2?.id)
val report2 = BankrollCalculator.computeReport(realm, brSetup2) val report2 = BankrollCalculator.computeReport(realm, brSetup2)
Assert.assertEquals(2000.0, report2.total, EPSILON) Assert.assertEquals(2000.0, report2.total, EPSILON)
@ -116,7 +116,7 @@ class BankrollInstrumentedUnitTest : SessionInstrumentedUnitTest() {
} }
val brSetup1 = BankrollReportSetup(br1) val brSetup1 = BankrollReportSetup(br1?.id)
val report1 = BankrollCalculator.computeReport(realm, brSetup1) val report1 = BankrollCalculator.computeReport(realm, brSetup1)
Assert.assertEquals(400.0, report1.total, EPSILON) Assert.assertEquals(400.0, report1.total, EPSILON)

@ -22,12 +22,11 @@ class CustomFieldFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {
val realm = this.mockRealm val realm = this.mockRealm
realm.beginTransaction() realm.beginTransaction()
val cf1 = CustomField() val cf1 = realm.createObject(CustomField::class.java, "1")
cf1.id = "1"
cf1.type = CustomField.Type.LIST.ordinal cf1.type = CustomField.Type.LIST.ordinal
val cfe1 = CustomFieldEntry() val cfe1 = realm.createObject(CustomFieldEntry::class.java, "9")
val cfe2 = CustomFieldEntry() val cfe2 = realm.createObject(CustomFieldEntry::class.java, "8")
cfe1.value = "super" cfe1.value = "super"
cfe2.value = "nul" cfe2.value = "nul"
@ -51,35 +50,38 @@ class CustomFieldFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {
@Test @Test
fun testCustomFieldAmountFilter() { fun testCustomFieldAmountFilter() {
val realm = this.mockRealm val cfId = "1234"
realm.beginTransaction() var s2Id = ""
val cf1 = CustomField() val realm = this.mockRealm
cf1.id = "1234" realm.executeTransaction {
cf1.type = CustomField.Type.AMOUNT.ordinal
val cf = realm.createObject(CustomField::class.java, cfId)
cf.type = CustomField.Type.AMOUNT.ordinal
val cfe1 = CustomFieldEntry() val cfe1 = realm.createObject(CustomFieldEntry::class.java, "999")
cfe1.id = "999" cf.entries.add(cfe1)
cf1.entries.add(cfe1) cfe1.numericValue = 30.0
cfe1.numericValue = 30.0
val cfe2 = CustomFieldEntry() val cfe2 = realm.createObject(CustomFieldEntry::class.java, "888")
cfe2.id = "888" cf.entries.add(cfe2)
cf1.entries.add(cfe2) cfe2.numericValue = 100.0
cfe2.numericValue = 100.0
val s1 = Session.testInstance(100.0, false, Date(), 1) val s1 = Session.testInstance(100.0, false, Date(), 1)
s1.customFieldEntries.add(cfe1) s1.customFieldEntries.add(cfe1)
val s2 = Session.testInstance(100.0, true, Date(), 1) val s2 = Session.testInstance(100.0, true, Date(), 1)
s2.customFieldEntries.add(cfe2) s2.customFieldEntries.add(cfe2)
realm.commitTransaction() s2Id = s2.id
}
val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.CustomFieldNumberQuery(cf1.id, 100.0))) val condition = QueryCondition.CustomFieldNumberQuery(cfId, 100.0)
val sessions = Filter.queryOn<Session>(realm, Query(condition))
Assert.assertEquals(1, sessions.size) Assert.assertEquals(1, sessions.size)
sessions[0]?.run { sessions.first()?.run {
Assert.assertEquals(s2.id, (this).id) Assert.assertEquals(s2Id, this.id)
} }
} }
} }

@ -2,18 +2,16 @@ package net.pokeranalytics.android.unitTests.filter
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import io.realm.RealmList
import io.realm.RealmResults
import net.pokeranalytics.android.R
import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest import net.pokeranalytics.android.components.BaseFilterInstrumentedUnitTest
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.realm.* import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.util.*
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class TransactionFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() { class TransactionFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {
@ -29,7 +27,7 @@ class TransactionFilterInstrumentedUnitTest : BaseFilterInstrumentedUnitTest() {
val name = "test" val name = "test"
type.name = name type.name = name
type.additive = value.additive type.additive = value.additive
type.kind = index type.kind = value.uniqueIdentifier
type.lock = true type.lock = true
realm.insertOrUpdate(type) realm.insertOrUpdate(type)
} }

@ -32,6 +32,13 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity>
<activity
android:name="net.pokeranalytics.android.ui.activity.ImportActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop">
<intent-filter tools:ignore="AppLinkUrlError"> <intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@ -45,23 +52,16 @@
</activity> </activity>
<activity
android:name="net.pokeranalytics.android.ui.activity.ImportActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop">
</activity>
<activity <activity
android:name="net.pokeranalytics.android.ui.activity.SessionActivity" android:name="net.pokeranalytics.android.ui.activity.SessionActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:windowSoftInputMode="adjustNothing" /> android:windowSoftInputMode="adjustNothing" />
<!-- No screenOrientation="portrait" to fix Oreo crash -->
<activity <activity
android:name="net.pokeranalytics.android.ui.activity.NewDataMenuActivity" android:name="net.pokeranalytics.android.ui.activity.NewDataMenuActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/PokerAnalyticsTheme.MenuDialog" /> android:theme="@style/PokerAnalyticsTheme.MenuDialog" />
<activity <activity
@ -74,6 +74,11 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name="net.pokeranalytics.android.ui.activity.Top10Activity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity <activity
android:name="net.pokeranalytics.android.ui.activity.SettingsActivity" android:name="net.pokeranalytics.android.ui.activity.SettingsActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
@ -109,6 +114,11 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name="net.pokeranalytics.android.ui.activity.FiltersListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity <activity
android:name="net.pokeranalytics.android.ui.activity.EditableDataActivity" android:name="net.pokeranalytics.android.ui.activity.EditableDataActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
@ -144,10 +154,10 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity <activity
android:name="net.pokeranalytics.android.ui.activity.TableReportActivity" android:name="net.pokeranalytics.android.ui.activity.TableReportActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<meta-data <meta-data
android:name="preloaded_fonts" android:name="preloaded_fonts"

@ -33,7 +33,7 @@ class PokerAnalyticsApplication : Application() {
Realm.init(this) Realm.init(this)
val realmConfiguration = RealmConfiguration.Builder() val realmConfiguration = RealmConfiguration.Builder()
.name(Realm.DEFAULT_REALM_NAME) .name(Realm.DEFAULT_REALM_NAME)
.schemaVersion(6) .schemaVersion(7)
.migration(PokerAnalyticsMigration()) .migration(PokerAnalyticsMigration())
.initialData(Seed(this)) .initialData(Seed(this))
.build() .build()
@ -72,10 +72,10 @@ class PokerAnalyticsApplication : Application() {
private fun createFakeSessions() { private fun createFakeSessions() {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val sessionsCount = realm.where<Session>().findAll().size val sessionsCount = realm.where<Session>().count()
realm.close() realm.close()
if (sessionsCount < 1) { if (sessionsCount < 10) {
GlobalScope.launch { GlobalScope.launch {
FakeDataManager.createFakeSessions(200) FakeDataManager.createFakeSessions(200)
} }

@ -4,6 +4,7 @@ import android.content.Context
import io.realm.Realm import io.realm.Realm
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat.* import net.pokeranalytics.android.calculus.Stat.*
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.combined import net.pokeranalytics.android.model.combined
import net.pokeranalytics.android.model.extensions.hourlyDuration import net.pokeranalytics.android.model.extensions.hourlyDuration
@ -95,7 +96,7 @@ class Calculator {
TABLE -> TableReportActivity::class.java TABLE -> TableReportActivity::class.java
PROGRESS -> ProgressReportActivity::class.java PROGRESS -> ProgressReportActivity::class.java
COMPARISON -> ComparisonReportActivity::class.java COMPARISON -> ComparisonReportActivity::class.java
else -> throw IllegalStateException("undefined activity for report display") else -> throw PAIllegalStateException("undefined activity for report display")
// MAP -> R.string.map // MAP -> R.string.map
// POLYNOMIAL -> null // POLYNOMIAL -> null
@ -378,7 +379,7 @@ class Calculator {
} }
val session = val session =
computable.session ?: throw IllegalStateException("Computing lone ComputableResult") computable.session ?: throw PAIllegalStateException("Computing lone ComputableResult")
results.addEvolutionValue(tSum, stat = NET_RESULT, data = session) results.addEvolutionValue(tSum, stat = NET_RESULT, data = session)
results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session) results.addEvolutionValue(tSum / index, stat = AVERAGE, data = session)
results.addEvolutionValue(index.toDouble(), stat = NUMBER_OF_GAMES, data = session) results.addEvolutionValue(index.toDouble(), stat = NUMBER_OF_GAMES, data = session)

@ -0,0 +1,34 @@
package net.pokeranalytics.android.calculus
import net.pokeranalytics.android.util.TextFormat
import java.util.*
/**
* ComputedStat contains a [stat] and their associated [value]
*/
class ComputedStat(var stat: Stat, var value: Double, var secondValue: Double? = null, var currency: Currency? = null) {
constructor(stat: Stat, value: Double, previousValue: Double?) : this(stat, value) {
if (previousValue != null) {
this.variation = (value - previousValue) / previousValue
}
}
/**
* The value used to get evolution dataset
*/
var progressValue: Double? = null
/**
* The variation of the stat
*/
var variation: Double? = null
/**
* Formats the value of the stat to be suitable for display
*/
fun format(): TextFormat {
return this.stat.format(this.value, this.secondValue, this.currency)
}
}

@ -5,9 +5,11 @@ import com.github.mikephil.charting.data.*
import io.realm.Realm import io.realm.Realm
import io.realm.RealmResults import io.realm.RealmResults
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
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.Timed import net.pokeranalytics.android.model.interfaces.GraphIdentifiableEntry
//import net.pokeranalytics.android.model.interfaces.Timed
import net.pokeranalytics.android.model.realm.ComputableResult import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
@ -73,7 +75,7 @@ class Report(var options: Calculator.Options) {
} }
} }
val label = statToUse?.name ?: "" val label = statToUse?.name ?: ""
return DataSetFactory.barDataSetInstance(entries, label, context) return DataSetFactory.barDataSetInstance(entries, label, context)
} }
@ -173,7 +175,8 @@ class ComputableGroup(var query: Query, var stats: List<Stat>? = null) {
} }
class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValues: Boolean = false) : GraphUnderlyingEntry { class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValues: Boolean = false) :
GraphUnderlyingEntry {
/** /**
* The session group used to computed the statIds * The session group used to computed the statIds
@ -195,7 +198,7 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu
/** /**
* Adds a value to the evolution values * Adds a value to the evolution values
*/ */
fun addEvolutionValue(value: Double, duration: Double? = null, stat: Stat, data: Timed) { fun addEvolutionValue(value: Double, duration: Double? = null, stat: Stat, data: GraphIdentifiableEntry) {
val point = if (duration != null) { val point = if (duration != null) {
Point(duration, y = value, data = data.objectIdentifier) Point(duration, y = value, data = data.objectIdentifier)
@ -322,7 +325,8 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu
computedStat.progressValue = Stat.returnOnInvestment(netResult, totalBuyin) computedStat.progressValue = Stat.returnOnInvestment(netResult, totalBuyin)
} }
} }
else -> {} else -> {
}
} }
} }
@ -437,7 +441,7 @@ class ComputedResults(group: ComputableGroup, shouldManageMultiGroupProgressValu
this.computedStat(stat)?.let { this.computedStat(stat)?.let {
return it.format() return it.format()
} ?: run { } ?: run {
throw IllegalStateException("Missing stat in results") throw PAIllegalStateException("Missing stat in results")
} }
} }

@ -3,6 +3,7 @@ package net.pokeranalytics.android.calculus
import android.content.Context import android.content.Context
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.FormattingException import net.pokeranalytics.android.exceptions.FormattingException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
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
@ -14,6 +15,7 @@ import net.pokeranalytics.android.util.extensions.formattedHourlyDuration
import net.pokeranalytics.android.util.extensions.toCurrency import net.pokeranalytics.android.util.extensions.toCurrency
import java.util.* import java.util.*
import kotlin.math.exp import kotlin.math.exp
import kotlin.math.pow
class StatFormattingException(message: String) : Exception(message) { class StatFormattingException(message: String) : Exception(message) {
@ -84,14 +86,14 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
return netBB / numberOfHands * 100 return netBB / numberOfHands * 100
} }
fun riskOfRuin(hourlyRate: Double, hourlyStandardDeviation: Double, bankrollValue: Double) : Double? { fun riskOfRuin(hourlyRate: Double, hourlyStandardDeviation: Double, bankrollValue: Double): Double? {
if (bankrollValue <= 0.0) { if (bankrollValue <= 0.0) {
return null return null
} }
val numerator = -2 * hourlyRate * bankrollValue val numerator = -2 * hourlyRate * bankrollValue
val denominator = Math.pow(hourlyStandardDeviation, 2.0) val denominator = hourlyStandardDeviation.pow(2.0)
val ratio = numerator / denominator val ratio = numerator / denominator
return exp(ratio) return exp(ratio)
@ -127,7 +129,7 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
MAXIMUM_DURATION -> R.string.longest_session MAXIMUM_DURATION -> R.string.longest_session
DAYS_PLAYED -> R.string.days_played DAYS_PLAYED -> R.string.days_played
TOTAL_BUYIN -> R.string.total_buyin TOTAL_BUYIN -> R.string.total_buyin
else -> throw IllegalStateException("Stat ${this.name} name required but undefined") else -> throw PAIllegalStateException("Stat ${this.name} name required but undefined")
} }
} }
@ -159,12 +161,17 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
HOURLY_DURATION, AVERAGE_HOURLY_DURATION, MAXIMUM_DURATION -> { HOURLY_DURATION, AVERAGE_HOURLY_DURATION, MAXIMUM_DURATION -> {
return TextFormat(value.formattedHourlyDuration()) return TextFormat(value.formattedHourlyDuration())
} // red/green percentages } // red/green percentages
WIN_RATIO, ROI, RISK_OF_RUIN -> { WIN_RATIO, ROI -> {
val color = if (value * 100 >= this.threshold) R.color.green else R.color.red val color = if (value * 100 >= this.threshold) R.color.green else R.color.red
return TextFormat("${(value * 100).formatted()}%", color) return TextFormat("${(value * 100).formatted()}%", color)
} // white amountsr }
RISK_OF_RUIN -> {
val color = if (value * 100 <= this.threshold) R.color.green else R.color.red
return TextFormat("${(value * 100).formatted()}%", color)
}
// white amountsr
AVERAGE_BUYIN, STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY, AVERAGE_BUYIN, STANDARD_DEVIATION, STANDARD_DEVIATION_HOURLY,
STANDARD_DEVIATION_BB_PER_100_HANDS -> { STANDARD_DEVIATION_BB_PER_100_HANDS, TOTAL_BUYIN -> {
return TextFormat(value.toCurrency(currency)) return TextFormat(value.toCurrency(currency))
} }
LONGEST_STREAKS -> { LONGEST_STREAKS -> {
@ -177,6 +184,7 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
private val threshold: Double private val threshold: Double
get() { get() {
return when (this) { return when (this) {
RISK_OF_RUIN -> 5.0
WIN_RATIO -> 50.0 WIN_RATIO -> 50.0
else -> 0.0 else -> 0.0
} }
@ -235,13 +243,24 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
} }
} }
val legendHideRightValue: Boolean
get() {
return when (this) {
AVERAGE, NUMBER_OF_SETS, NUMBER_OF_GAMES, WIN_RATIO,
HOURLY_DURATION, AVERAGE_HOURLY_DURATION -> true
else -> false
}
}
/** /**
* Returns if the stat has a significant value to display in a progress graph * Returns if the stat has a significant value to display in a progress graph
*/ */
val graphSignificantIndividualValue: Boolean val graphSignificantIndividualValue: Boolean
get() { get() {
return when (this) { return when (this) {
WIN_RATIO, NUMBER_OF_SETS, NUMBER_OF_GAMES, STANDARD_DEVIATION, HOURLY_DURATION -> false AVERAGE, WIN_RATIO, NUMBER_OF_SETS, NUMBER_OF_GAMES,
STANDARD_DEVIATION, HOURLY_DURATION -> false
else -> true else -> true
} }
} }
@ -294,33 +313,3 @@ enum class Stat(override var uniqueIdentifier: Int) : IntIdentifiable, RowRepres
override val viewType: Int = RowViewType.TITLE_VALUE.ordinal override val viewType: Int = RowViewType.TITLE_VALUE.ordinal
} }
/**
* ComputedStat contains a [stat] and their associated [value]
*/
class ComputedStat(var stat: Stat, var value: Double, var secondValue: Double? = null, var currency: Currency? = null) {
constructor(stat: Stat, value: Double, previousValue: Double?) : this(stat, value) {
if (previousValue != null) {
this.variation = (value - previousValue) / previousValue
}
}
/**
* The value used to get evolution dataset
*/
var progressValue: Double? = null
/**
* The variation of the stat
*/
var variation: Double? = null
/**
* Formats the value of the stat to be suitable for display
*/
fun format(): TextFormat {
return this.stat.format(this.value, this.secondValue, this.currency)
}
}

@ -5,7 +5,9 @@ import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.ComputedResults import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.util.extensions.findById
class BankrollCalculator { class BankrollCalculator {
@ -13,12 +15,15 @@ class BankrollCalculator {
fun computeReport(realm: Realm, setup: BankrollReportSetup) : BankrollReport { fun computeReport(realm: Realm, setup: BankrollReportSetup) : BankrollReport {
//val realm = Realm.getDefaultInstance()
val report = BankrollReport(setup) val report = BankrollReport(setup)
realm.refresh() // fixes an issue where a newly created bankroll is not found, throwing an exception
val bankrolls: List<Bankroll> = val bankrolls: List<Bankroll> =
if (setup.bankroll != null) listOf(setup.bankroll) if (setup.bankrollId != null) {
val bankroll = realm.findById<Bankroll>(setup.bankrollId) ?: throw PAIllegalStateException("Bankroll not found with id=${setup.bankrollId}")
report.currency = bankroll.utilCurrency
listOf(bankroll)
}
else realm.where(Bankroll::class.java).findAll() else realm.where(Bankroll::class.java).findAll()
var initialValue = 0.0 var initialValue = 0.0
@ -41,7 +46,7 @@ class BankrollCalculator {
report.transactionsNet = transactionNet report.transactionsNet = transactionNet
report.initial = initialValue report.initial = initialValue
val query = setup.query val query = setup.query(realm)
val transactions = Filter.queryOn<Transaction>(realm, query) val transactions = Filter.queryOn<Transaction>(realm, query)
report.addDatedItems(transactions) report.addDatedItems(transactions)

@ -3,54 +3,28 @@ package net.pokeranalytics.android.calculus.bankroll
import android.content.Context import android.content.Context
import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.data.LineDataSet
import io.realm.Realm
import net.pokeranalytics.android.exceptions.PAIllegalStateException
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.DatedValue import net.pokeranalytics.android.model.interfaces.DatedBankrollGraphEntry
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.ui.graph.DataSetFactory import net.pokeranalytics.android.ui.graph.DataSetFactory
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.ui.view.RowViewType
import java.util.* import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
//object BankrollReportManager {
//
// var mainReport: BankrollReport? = null
// var reports: MutableMap<String, BankrollReport> = mutableMapOf()
//
// fun udpateBankrolls(bankrolls: List<Bankroll>) {
// this.invalidateMainReport()
// bankrolls.forEach {
// this.reports.remove(it.id)
// }
// }
//
// fun deleteBankrolls(bankrolls: List<Bankroll>) {
// this.invalidateMainReport()
// bankrolls.forEach {
// this.reports.remove(it.id)
// }
// }
//
// private fun invalidateMainReport() {
// this.mainReport = null
// }
//
// private fun launchReports(bankrolls: List<Bankroll>) {
//
// this.mainReport = BankrollCalculator.computeReport()
//
//
// }
//
//}
/** /**
* This class holds the results from the BankrollCalculator computations * This class holds the results from the BankrollCalculator computations
* It has all the information required for the Bankroll various displays * It has all the information required for the Bankroll various displays
*/ */
class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable { class BankrollReport(var setup: BankrollReportSetup) {
/**
* The java.util.Currency
*/
var currency: Currency? = null
/** /**
* The value of the bankroll * The value of the bankroll
@ -148,21 +122,12 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
/** /**
* The list of dated items used for the graph * The list of dated items used for the graph
*/ */
private var evolutionItems: MutableList<DatedValue> = mutableListOf() private var evolutionItems: MutableList<DatedBankrollGraphEntry> = mutableListOf()
override val viewType: Int
get() {
return if (setup.bankroll == null) {
RowViewType.LEGEND_DEFAULT.ordinal
} else {
RowViewType.TITLE_VALUE_ARROW.ordinal
}
}
/** /**
* Adds a list of dated items to the evolution items used to get the bankroll graph * Adds a list of dated items to the evolution items used to get the bankroll graph
*/ */
fun addDatedItems(items: Collection<DatedValue>) { fun addDatedItems(items: Collection<DatedBankrollGraphEntry>) {
this.evolutionItems.addAll(items) this.evolutionItems.addAll(items)
} }
@ -176,7 +141,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
var bucket = this.transactionBuckets[type.id] var bucket = this.transactionBuckets[type.id]
if (bucket == null) { if (bucket == null) {
val b = TransactionBucket(this.setup.virtualBankroll) val b = TransactionBucket(type.name, this.setup.virtualBankroll)
this.transactionBuckets[type.id] = b this.transactionBuckets[type.id] = b
bucket = b bucket = b
} }
@ -184,7 +149,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
bucket.addTransaction(transaction) bucket.addTransaction(transaction)
} ?: run { } ?: run {
throw IllegalStateException("Transaction has no type") throw PAIllegalStateException("Transaction has no type")
} }
} }
@ -200,10 +165,14 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
this.evolutionItems.sortBy { it.date } this.evolutionItems.sortBy { it.date }
var total = 0.0 var total = this.initial
this.evolutionItems.forEach { this.evolutionItems.forEach {
total += it.amount val rate = it.bankroll?.rate ?: 1.0
val point = BRGraphPoint(total, it.date, it)
// Timber.d("rate = $rate, amount = ${it.amount}")
total += it.amount * rate
// Timber.d("total = $total")
val point = BRGraphPoint(total, it.date, it.objectIdentifier)
this.evolutionPoints.add(point) this.evolutionPoints.add(point)
} }
@ -228,7 +197,7 @@ class BankrollReport(var setup: BankrollReportSetup) : RowRepresentable {
* A class describing the parameters required to launch a bankroll report * A class describing the parameters required to launch a bankroll report
* *
*/ */
class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null, val to: Date? = null) { class BankrollReportSetup(val bankrollId: String? = null, val from: Date? = null, val to: Date? = null) {
/** /**
* Returns whether the setup concerns the virtual bankroll, * Returns whether the setup concerns the virtual bankroll,
@ -236,32 +205,32 @@ class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null
*/ */
val virtualBankroll: Boolean val virtualBankroll: Boolean
get() { get() {
return this.bankroll == null return this.bankrollId == null
} }
/** /**
* the query used to get bankroll transactions * the query used to get bankroll transactions
*/ */
val query: Query fun query(realm: Realm): Query {
get() { val query = Query()
val query = Query()
this.bankroll?.let { this.bankrollId?.let {
val bankrollCondition = QueryCondition.AnyBankroll(bankroll) val bankroll = realm.findById<Bankroll>(it) ?: throw PAIllegalStateException("Bankroll not found with id $it")
query.add(bankrollCondition) val bankrollCondition = QueryCondition.AnyBankroll(bankroll)
} query.add(bankrollCondition)
this.from?.let { }
val fromCondition = QueryCondition.StartedFromDate() this.from?.let {
fromCondition.singleValue = it val fromCondition = QueryCondition.StartedFromDate()
query.add(fromCondition) fromCondition.singleValue = it
} query.add(fromCondition)
this.to?.let {
val toCondition = QueryCondition.StartedToDate()
toCondition.singleValue = it
query.add(toCondition)
}
return query
} }
this.to?.let {
val toCondition = QueryCondition.StartedToDate()
toCondition.singleValue = it
query.add(toCondition)
}
return query
}
/** /**
* Returns whether or not the initial value should be added for the bankroll total * Returns whether or not the initial value should be added for the bankroll total
@ -276,7 +245,7 @@ class BankrollReportSetup(val bankroll: Bankroll? = null, val from: Date? = null
/** /**
* A TransactionBucket holds a list of _transactions and computes its amount sum * A TransactionBucket holds a list of _transactions and computes its amount sum
*/ */
class TransactionBucket(useRate: Boolean = false) { class TransactionBucket(var name: String, useRate: Boolean = false) {
/** /**
* Whether the bankroll rate should be used * Whether the bankroll rate should be used
@ -317,6 +286,6 @@ class TransactionBucket(useRate: Boolean = false) {
data class BRGraphPoint(var value: Double, var date: Date, var data: Any? = null) { data class BRGraphPoint(var value: Double, var date: Date, var data: Any? = null) {
var variation: Double = 0.0 // var variation: Double = 0.0
} }

@ -0,0 +1,118 @@
package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Transaction
import timber.log.Timber
import java.util.*
import kotlin.coroutines.CoroutineContext
object BankrollReportManager {
val coroutineContext: CoroutineContext
get() = Dispatchers.Main
private var reports: MutableMap<String?, BankrollReport> = mutableMapOf()
private var computableResults: RealmResults<ComputableResult>
private var bankrolls: RealmResults<Bankroll>
private var transactions: RealmResults<Transaction>
init {
val realm = Realm.getDefaultInstance()
computableResults = realm.where(ComputableResult::class.java).findAll()
bankrolls = realm.where(Bankroll::class.java).findAll()
transactions = realm.where(Transaction::class.java).findAll()
initializeListeners()
realm.close()
}
/**
* Listens to all objects that might have an impact on any bankroll report
*/
private fun initializeListeners() {
this.computableResults.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it]?.session?.bankroll }.toSet()
this.updateBankrolls(bankrolls)
}
this.bankrolls.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it] }.toSet()
this.updateBankrolls(bankrolls)
}
this.transactions.addChangeListener { t, changeSet ->
val indexes = changeSet.changes.plus(changeSet.insertions).toList()
val bankrolls = indexes.mapNotNull { t[it]?.bankroll }.toSet()
this.updateBankrolls(bankrolls)
}
}
fun reportForBankroll(bankrollId: String?, handler: (BankrollReport) -> Unit) {
Timber.d("Request bankroll report for bankrollId = $bankrollId")
// if the report exists, return it
val existingReport: BankrollReport? = this.reports[bankrollId]
if (existingReport != null) {
handler(existingReport)
return
}
// otherwise compute it
GlobalScope.launch(coroutineContext) {
var report: BankrollReport? = null
val coroutine = GlobalScope.async {
val s = Date()
Timber.d(">>>>> start computing bankroll...")
val realm = Realm.getDefaultInstance()
val setup = BankrollReportSetup(bankrollId)
report = BankrollCalculator.computeReport(realm, setup)
realm.close()
val e = Date()
val duration = (e.time - s.time) / 1000.0
Timber.d(">>>>> ended in $duration seconds")
}
coroutine.await()
report?.let {
handler(it)
}
}
}
/**
* Notifies the manager of cases not managed by RealmResults listener, such as deletions
*/
fun notifyBankrollReportImpact(bankrollId: String) {
this.reports.remove(bankrollId)
this.reports.remove(null)
}
private fun updateBankrolls(bankrolls: Set<Bankroll>) {
this.invalidateReport(bankrolls)
}
private fun invalidateReport(bankrolls: Set<Bankroll>) {
this.reports.remove(null)
bankrolls.forEach { br ->
this.reports.remove(br.id)
}
}
}

@ -10,6 +10,7 @@ class ConfigurationException(message: String) : Exception(message)
class EnumIdentifierNotFoundException(message: String) : Exception(message) class EnumIdentifierNotFoundException(message: String) : Exception(message)
class MisconfiguredSavableEnumException(message: String) : Exception(message) class MisconfiguredSavableEnumException(message: String) : Exception(message)
class PAIllegalStateException(message: String) : Exception(message)
sealed class PokerAnalyticsException(message: String) : Exception(message) { sealed class PokerAnalyticsException(message: String) : Exception(message) {
object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition") object FilterElementUnknownName : PokerAnalyticsException(message = "No filterElement name was found to identify the queryCondition")
@ -20,6 +21,8 @@ sealed class PokerAnalyticsException(message: String) : Exception(message) {
object QueryTypeUnhandled: PokerAnalyticsException(message = "queryWith type not handled") object QueryTypeUnhandled: PokerAnalyticsException(message = "queryWith type not handled")
object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected") object QueryValueMapUnexpectedValue: PokerAnalyticsException(message = "valueMap null not expected")
object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "queryWith is empty or null") object FilterElementExpectedValueMissing : PokerAnalyticsException(message = "queryWith is empty or null")
object InputFragmentException : PokerAnalyticsException(message = "RowEditableDelegate must be a Fragment")
object DateTimePickerException: PokerAnalyticsException(message = "DataSource is not a DateRowEditableDescriptor")
data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith element '$filterElementRow' type is missing") data class FilterElementTypeMissing(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "queryWith element '$filterElementRow' type is missing")
data class QueryValueMapMissingKeys(val missingKeys: List<String>) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys") data class QueryValueMapMissingKeys(val missingKeys: List<String>) : PokerAnalyticsException(message = "valueMap does not contain $missingKeys")
data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no queryWith type for $filterElementRow") data class UnknownQueryTypeForRow(val filterElementRow: FilterElementRow) : PokerAnalyticsException(message = "no queryWith type for $filterElementRow")

@ -4,6 +4,7 @@ import io.realm.Realm
import io.realm.Sort import io.realm.Sort
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.exceptions.PokerAnalyticsException import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.Criteria.Bankrolls.comparison import net.pokeranalytics.android.model.Criteria.Bankrolls.comparison
import net.pokeranalytics.android.model.Criteria.Blinds.comparison import net.pokeranalytics.android.model.Criteria.Blinds.comparison
@ -329,7 +330,7 @@ interface CustomFieldCriteria {
var customFieldId: String var customFieldId: String
fun customField(realm: Realm) : CustomField { fun customField(realm: Realm) : CustomField {
return realm.findById(this.customFieldId) ?: throw IllegalStateException("Custom field not found") return realm.findById(this.customFieldId) ?: throw PAIllegalStateException("Custom field not found")
} }
fun customFieldType(realm: Realm): Int { fun customFieldType(realm: Realm): Int {

@ -1,6 +1,10 @@
package net.pokeranalytics.android.model.extensions package net.pokeranalytics.android.model.extensions
import android.content.Context
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.TournamentType
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.extensions.toCurrency
import java.util.* import java.util.*
enum class SessionState { enum class SessionState {
@ -39,7 +43,45 @@ fun Session.getState(): SessionState {
return SessionState.STARTED return SessionState.STARTED
} }
} }
}
/**
* Formate the session game type
*/
fun Session.getFormattedGameType(context: Context): String {
var parameters = mutableListOf<String>()
if (isTournament()) {
tournamentEntryFee?.let {
parameters.add(it.toCurrency(currency))
}
tournamentName?.let {
parameters.add(it.name)
} ?: run {
parameters.add(getFormattedGame())
tournamentType?.let { type ->
parameters.add(TournamentType.values()[type].localizedTitle(context))
}
}
if (parameters.size == 0) {
parameters.add(context.getString(R.string.tournament).capitalize())
}
} else {
if (cgSmallBlind != null && cgBigBlind != null) {
parameters.add(getFormattedBlinds())
}
game?.let {
parameters.add(getFormattedGame())
}
if (parameters.size == 0) {
parameters.add(context.getString(R.string.cash_game).capitalize())
}
}
return parameters.joinToString(separator = " ")
} }
val AbstractList<Session>.hourlyDuration: Double val AbstractList<Session>.hourlyDuration: Double
@ -95,4 +137,4 @@ fun MutableList<TimeInterval>.update(timeInterval: TimeInterval): MutableList<Ti
} }
return this return this
} }

@ -2,7 +2,6 @@ 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
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
@ -77,7 +76,7 @@ class Query {
} }
} }
//println("<<<<<< ${realmQuery}") // println("<<<<<< ${realmQuery.description}")
val queryLast = this.conditions.filter { val queryLast = this.conditions.filter {
it is QueryCondition.Last it is QueryCondition.Last
}.firstOrNull() }.firstOrNull()

@ -15,7 +15,7 @@ import net.pokeranalytics.android.model.TournamentType
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.realm.* import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterElementRow
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterSectionRow
@ -89,7 +89,7 @@ sealed class QueryCondition : FilterElementRow {
EQUALS, EQUALS,
TRUE, TRUE,
; ;
} }
val baseId = this::class.simpleName ?: throw PokerAnalyticsException.FilterElementUnknownName val baseId = this::class.simpleName ?: throw PokerAnalyticsException.FilterElementUnknownName
@ -257,7 +257,7 @@ sealed class QueryCondition : FilterElementRow {
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val entityName = entityName(realm) val entityName = entityName(realm, context)
val completeLabel = when (listOfValues.size) { val completeLabel = when (listOfValues.size) {
0 -> NULL_TEXT 0 -> NULL_TEXT
1, 2 -> { 1, 2 -> {
@ -269,9 +269,9 @@ sealed class QueryCondition : FilterElementRow {
return completeLabel return completeLabel
} }
open fun entityName(realm: Realm): String { open fun entityName(realm: Realm, context: Context): String {
return baseId return entityName(context)
} }
private fun labelForValue(realm: Realm, value: String): String { private fun labelForValue(realm: Realm, value: String): String {
val query = realm.where(entity) val query = realm.where(entity)
@ -320,7 +320,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(bankroll: Bankroll) : this() { constructor(bankroll: Bankroll) : this() {
this.setObject(bankroll) this.setObject(bankroll)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.bankrolls)
}
}
class AnyGame() : QueryDataCondition<Game>() { class AnyGame() : QueryDataCondition<Game>() {
override val entity: Class<Game> = Game::class.java override val entity: Class<Game> = Game::class.java
@ -328,7 +332,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(game: Game) : this() { constructor(game: Game) : this() {
this.setObject(game) this.setObject(game)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.games)
}
}
class AnyTournamentName() : QueryDataCondition<TournamentName>() { class AnyTournamentName() : QueryDataCondition<TournamentName>() {
override val entity: Class<TournamentName> = TournamentName::class.java override val entity: Class<TournamentName> = TournamentName::class.java
@ -336,7 +344,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(tournamentName: TournamentName) : this() { constructor(tournamentName: TournamentName) : this() {
this.setObject(tournamentName) this.setObject(tournamentName)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.tournament_names)
}
}
class AnyTournamentFeature() : QueryDataCondition<TournamentFeature>() { class AnyTournamentFeature() : QueryDataCondition<TournamentFeature>() {
override val entity: Class<TournamentFeature> = TournamentFeature::class.java override val entity: Class<TournamentFeature> = TournamentFeature::class.java
@ -344,7 +356,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(tournamentFeature: TournamentFeature) : this() { constructor(tournamentFeature: TournamentFeature) : this() {
this.setObject(tournamentFeature) this.setObject(tournamentFeature)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.tournament_features)
}
}
class AllTournamentFeature() : QueryDataCondition<TournamentFeature>() { class AllTournamentFeature() : QueryDataCondition<TournamentFeature>() {
override var operator = Operator.ALL override var operator = Operator.ALL
@ -353,7 +369,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(tournamentFeature: TournamentFeature) : this() { constructor(tournamentFeature: TournamentFeature) : this() {
this.setObject(tournamentFeature) this.setObject(tournamentFeature)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.tournament_features)
}
}
class AnyLocation() : QueryDataCondition<Location>() { class AnyLocation() : QueryDataCondition<Location>() {
override val entity: Class<Location> = Location::class.java override val entity: Class<Location> = Location::class.java
@ -361,7 +381,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(location: Location) : this() { constructor(location: Location) : this() {
this.setObject(location) this.setObject(location)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.locations)
}
}
class AnyTransactionType() : QueryDataCondition<TransactionType>() { class AnyTransactionType() : QueryDataCondition<TransactionType>() {
override val entity: Class<TransactionType> = TransactionType::class.java override val entity: Class<TransactionType> = TransactionType::class.java
@ -369,27 +393,47 @@ sealed class QueryCondition : FilterElementRow {
constructor(transactionType: TransactionType) : this() { constructor(transactionType: TransactionType) : this() {
this.setObject(transactionType) this.setObject(transactionType)
} }
}
override fun entityName(context: Context): String {
return context.getString(R.string.operation_types)
}
}
class AnyLimit : ListOfInt() { class AnyLimit : ListOfInt() {
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return Limit.values()[value].getDisplayName(context) return Limit.values()[value].getDisplayName(context)
} }
override fun entityName(context: Context): String {
return context.getString(R.string.limits)
}
} }
class AnyTableSize : ListOfInt() { class AnyTableSize : ListOfInt() {
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return TableSize(value).getDisplayName(context) return TableSize(value).getDisplayName(context)
} }
override fun entityName(context: Context): String {
return context.getString(R.string.table_sizes)
}
} }
class AnyTournamentType : ListOfInt() { class AnyTournamentType : ListOfInt() {
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return TournamentType.values()[value].getDisplayName(context) return TournamentType.values()[value].getDisplayName(context)
} }
override fun entityName(context: Context): String {
return context.getString(R.string.tournament_types)
}
} }
class AnyBlind : ListOfString() class AnyBlind : ListOfString() {
override fun entityName(context: Context): String {
return context.getString(R.string.blinds)
}
}
object Last : SingleInt() { object Last : SingleInt() {
override var operator = Operator.EQUALS override var operator = Operator.EQUALS
@ -549,7 +593,7 @@ sealed class QueryCondition : FilterElementRow {
} }
override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal override val viewType: Int = RowViewType.TITLE_VALUE_CHECK.ordinal
override val bottomSheetType: BottomSheetType = BottomSheetType.DOUBLE_EDIT_TEXT override val inputFragmentType: InputFragmentType = InputFragmentType.DOUBLE_EDIT_TEXT
override fun labelForValue(value: Int, context: Context): String { override fun labelForValue(value: Int, context: Context): String {
return value.toMinutes(context) return value.toMinutes(context)
@ -645,11 +689,11 @@ sealed class QueryCondition : FilterElementRow {
constructor(customFieldEntry: CustomFieldEntry) : this() { constructor(customFieldEntry: CustomFieldEntry) : this() {
this.setObject(customFieldEntry) this.setObject(customFieldEntry)
this.customFieldId = customFieldEntry.customFields?.firstOrNull()?.id this.customFieldId = customFieldEntry.customField?.id
?: throw PokerAnalyticsException.QueryValueMapUnexpectedValue ?: throw PokerAnalyticsException.QueryValueMapUnexpectedValue
} }
override fun entityName(realm: Realm): String { override fun entityName(realm: Realm, context: Context): String {
return customFieldName(realm) return customFieldName(realm)
} }
@ -668,6 +712,7 @@ sealed class QueryCondition : FilterElementRow {
realmQuery: RealmQuery<T>, realmQuery: RealmQuery<T>,
otherQueryCondition: QueryCondition? = null otherQueryCondition: QueryCondition? = null
): RealmQuery<T> { ): RealmQuery<T> {
val fieldName = FilterHelper.fieldNameForQueryType<T>(this::class.java) val fieldName = FilterHelper.fieldNameForQueryType<T>(this::class.java)
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown fieldName ?: throw PokerAnalyticsException.QueryValueMapUnknown
@ -720,12 +765,14 @@ sealed class QueryCondition : FilterElementRow {
return realmQuery return realmQuery
} }
is DuringThisWeek -> { is DuringThisWeek -> {
val startDate = Date()
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
calendar.time = startDate calendar.set(Calendar.HOUR_OF_DAY, 0)
calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.SUNDAY) calendar.clear(Calendar.MINUTE)
calendar.clear(Calendar.SECOND)
calendar.clear(Calendar.MILLISECOND)
calendar.set(Calendar.DAY_OF_WEEK, calendar.firstDayOfWeek)
return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and() return realmQuery.greaterThanOrEqualTo(fieldName, calendar.time.startOfDay()).and()
.lessThanOrEqualTo(fieldName, startDate.endOfDay()) .lessThanOrEqualTo(fieldName, Date().endOfDay())
} }
is DuringThisMonth -> { is DuringThisMonth -> {
val startDate = Date() val startDate = Date()
@ -905,15 +952,15 @@ sealed class QueryCondition : FilterElementRow {
} }
} }
override val bottomSheetType: BottomSheetType override val inputFragmentType: InputFragmentType
get() { get() {
return when (this) { return when (this) {
is PastDay -> BottomSheetType.EDIT_TEXT is PastDay -> InputFragmentType.EDIT_TEXT
else -> { else -> {
when (this.operator) { when (this.operator) {
Operator.MORE -> BottomSheetType.EDIT_TEXT Operator.MORE -> InputFragmentType.EDIT_TEXT
Operator.LESS -> BottomSheetType.EDIT_TEXT Operator.LESS -> InputFragmentType.EDIT_TEXT
else -> BottomSheetType.NONE else -> InputFragmentType.NONE
} }
} }
} }
@ -939,7 +986,7 @@ 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 TournamentNumberOfPlayer -> { is TournamentNumberOfPlayer -> {
when (this.operator) { when (this.operator) {
Operator.MORE -> R.string.minimum Operator.MORE -> R.string.minimum
Operator.LESS -> R.string.maximum Operator.LESS -> R.string.maximum

@ -1,5 +1,7 @@
package net.pokeranalytics.android.model.interfaces package net.pokeranalytics.android.model.interfaces
import io.realm.RealmModel
/** /**
* An interface to be able to track the usage of an object * An interface to be able to track the usage of an object
*/ */
@ -7,4 +9,6 @@ interface CountableUsage : Identifiable {
var useCount: Int var useCount: Int
get() { return 0 } get() { return 0 }
set(_) {} set(_) {}
val ownerClass: Class<out RealmModel>
} }

@ -1,5 +1,6 @@
package net.pokeranalytics.android.model.interfaces package net.pokeranalytics.android.model.interfaces
import net.pokeranalytics.android.model.realm.Bankroll
import java.util.* import java.util.*
interface Dated { interface Dated {
@ -12,4 +13,10 @@ interface DatedValue : Dated {
var amount: Double var amount: Double
} }
interface DatedBankrollGraphEntry : DatedValue, GraphIdentifiableEntry {
var bankroll: Bankroll?
}

@ -45,6 +45,7 @@ interface NameManageable : Manageable {
} }
} }
class ObjectIdentifier(var id: String, var clazz: Class<out Identifiable>)
/** /**
* An interface associate a unique uniqueIdentifier to an object * An interface associate a unique uniqueIdentifier to an object
@ -55,6 +56,16 @@ interface Identifiable : RealmModel {
* A unique uniqueIdentifier getter * A unique uniqueIdentifier getter
*/ */
var id: String var id: String
/**
* required because "this.class" returns the proxy class, making where<class> queries crash
*/
val realmObjectClass: Class<out Identifiable>
val objectIdentifier: ObjectIdentifier
get() {
return ObjectIdentifier(this.id, this.realmObjectClass)
}
} }
/** /**
@ -125,6 +136,6 @@ interface Deletable : Identifiable {
/** /**
* A method to override if we need to delete linked objects or other stuff * A method to override if we need to delete linked objects or other stuff
*/ */
fun deleteDependencies() {} fun deleteDependencies(realm: Realm) {}
} }

@ -1,10 +1,11 @@
package net.pokeranalytics.android.model.interfaces package net.pokeranalytics.android.model.interfaces
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import java.util.* import java.util.*
interface Timed : GraphUnderlyingEntry, Identifiable { interface GraphIdentifiableEntry : GraphUnderlyingEntry, Identifiable
interface Timed : GraphIdentifiableEntry {
fun startDate() : Date? fun startDate() : Date?
@ -30,6 +31,4 @@ interface Timed : GraphUnderlyingEntry, Identifiable {
val hourlyDuration: Double val hourlyDuration: Double
get() = this.netDuration / 3600000.0 get() = this.netDuration / 3600000.0
val objectIdentifier : ObjectIdentifier
} }

@ -5,6 +5,7 @@ import io.realm.Realm
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.realm.* import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.Preferences
class Patcher { class Patcher {
@ -19,7 +20,23 @@ class Patcher {
Preferences.executeOnce(Preferences.Keys.PATCH_TRANSACTION_TYPES_NAMES, context) { Preferences.executeOnce(Preferences.Keys.PATCH_TRANSACTION_TYPES_NAMES, context) {
patchDefaultTransactionTypes(context) patchDefaultTransactionTypes(context)
} }
Preferences.executeOnce(Preferences.Keys.PATCH_BLINDS_FORMAT, context) {
patchBlindFormat()
}
val realm = Realm.getDefaultInstance()
val lockedTypes = realm.where(TransactionType::class.java).equalTo("lock", true).findAll()
if (lockedTypes.size == 3) {
Preferences.executeOnce(Preferences.Keys.ADD_NEW_TRANSACTION_TYPES, context) {
val newTypes = arrayOf(TransactionType.Value.STACKING_INCOMING, TransactionType.Value.STACKING_OUTGOING)
realm.executeTransaction {
Seed.createDefaultTransactionTypes(newTypes, context, realm)
}
}
}
realm.close()
} }
private fun patchBreaks() { private fun patchBreaks() {
@ -40,6 +57,7 @@ class Patcher {
it.computeNumberOfRebuy() it.computeNumberOfRebuy()
} }
} }
realm.close() realm.close()
} }
@ -58,6 +76,17 @@ class Patcher {
realm.close() realm.close()
} }
private fun patchBlindFormat() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val sessions = realm.where(Session::class.java).findAll()
sessions.forEach { session ->
session.formatBlinds()
}
}
realm.close()
}
} }
} }

@ -144,6 +144,13 @@ class PokerAnalyticsMigration : RealmMigration {
schema.get("Filter")?.removeField("usageCount") schema.get("Filter")?.removeField("usageCount")
currentVersion++ currentVersion++
} }
// Migrate to version 7
if (currentVersion == 6) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("TransactionType")?.addField("useCount", Int::class.java)
currentVersion++
}
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

@ -4,16 +4,18 @@ import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where 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.Identifiable
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.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.util.UserDefaults
import java.util.* import java.util.*
open class Bankroll : RealmObject(), NameManageable, RowRepresentable { open class Bankroll : RealmObject(), NameManageable, RowRepresentable {
@ -40,16 +42,25 @@ open class Bankroll : RealmObject(), NameManageable, RowRepresentable {
val rate: Double val rate: Double
get() { get() {
return this.currency?.rate ?: 1.0 return this.currency?.rate ?: Currency.DEFAULT_RATE
} }
val javaCurrency: java.util.Currency
get() {
return currency?.code?.let {
java.util.Currency.getInstance(it)
} ?: run {
UserDefaults.currency
}
}
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
return this.name return this.name
} }
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" BankrollRow.NAME -> this.name = value as String? ?: ""
BankrollRow.LIVE -> { BankrollRow.LIVE -> {
this.live = if (value is Boolean) !value else false this.live = if (value is Boolean) !value else false
} }
@ -118,4 +129,15 @@ open class Bankroll : RealmObject(), NameManageable, RowRepresentable {
} }
val utilCurrency: java.util.Currency
get() {
this.currency?.code?.let {
return java.util.Currency.getInstance(it)
}
return UserDefaults.currency
}
@Ignore
override val realmObjectClass: Class<out Identifiable> = Bankroll::class.java
} }

@ -20,14 +20,10 @@ open class ComputableResult() : RealmObject(), Filterable {
var bbPer100Hands: BB = 0.0 var bbPer100Hands: BB = 0.0
// var sessionSet: SessionSet? = null
var session: Session? = null var session: Session? = null
fun updateWith(session: Session) { fun updateWith(session: Session) {
// this.sessionSet = session.sessionSet
val rate = session.bankroll?.currency?.rate ?: 1.0 val rate = session.bankroll?.currency?.rate ?: 1.0
session.result?.let { result -> session.result?.let { result ->
@ -50,7 +46,7 @@ open class ComputableResult() : RealmObject(), Filterable {
IS_POSITIVE("isPositive"), IS_POSITIVE("isPositive"),
RATED_BUYIN("ratedBuyin"), RATED_BUYIN("ratedBuyin"),
ESTIMATED_HANDS("estimatedHands"), ESTIMATED_HANDS("estimatedHands"),
BB_PER100HANDS("bbPer100Hands") // BB_PER100HANDS("bbPer100Hands")
} }
companion object { companion object {

@ -8,8 +8,12 @@ import java.util.*
open class Currency : RealmObject() { open class Currency : RealmObject() {
@Ignore companion object {
val DEFAULTRATE: Double = 1.0
@Ignore
val DEFAULT_RATE: Double = 1.0
}
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() var id = UUID.randomUUID().toString()
@ -22,11 +26,11 @@ open class Currency : RealmObject() {
/** /**
* The rate of the currency with the main currency * The rate of the currency with the main currency
*/ */
var rate: Double? = DEFAULTRATE var rate: Double? = DEFAULT_RATE
fun refreshRelatedRatedValues() { fun refreshRelatedRatedValues() {
val rate = this.rate ?: DEFAULTRATE val rate = this.rate ?: DEFAULT_RATE
val query = this.realm.where(ComputableResult::class.java) val query = this.realm.where(ComputableResult::class.java)
query.`in`("session.bankroll.currency.id", arrayOf(this.id)) query.`in`("session.bankroll.currency.id", arrayOf(this.id))
val cResults = query.findAll() val cResults = query.findAll()

@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import android.text.InputType import android.text.InputType
import androidx.fragment.app.Fragment
import io.realm.Realm import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
@ -11,16 +12,18 @@ import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
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.RowRepresentableDelegate
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.fragment.components.input.InputFragment
import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
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.RowEditableDataSource
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.util.enumerations.IntIdentifiable import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -28,6 +31,9 @@ import kotlin.collections.ArrayList
open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {
@Ignore
override val realmObjectClass: Class<out Identifiable> = CustomField::class.java
/** /**
* The custom field type: a list of items, a number or an amont * The custom field type: a list of items, a number or an amont
*/ */
@ -59,7 +65,8 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
var type: Int = Type.LIST.uniqueIdentifier var type: Int = Type.LIST.uniqueIdentifier
set(value) { set(value) {
if (field == Type.LIST.uniqueIdentifier && value != Type.LIST.uniqueIdentifier) { if (field == Type.LIST.uniqueIdentifier && value != Type.LIST.uniqueIdentifier) {
this.removeListEntries() this.entriesToDelete.addAll(this.entries)
this.entries.clear()
} }
field = value field = value
@ -120,9 +127,23 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
return rowRepresentation return rowRepresentation
} }
override fun startEditing(dataSource: Any?, parent: Fragment?) {
if (dataSource == null) return
if (dataSource !is Session) return
if (parent == null) return
if (parent !is RowRepresentableDelegate) return
val data = RowEditableDataSource()
when (type) {
Type.LIST.uniqueIdentifier -> data.append(dataSource.customFieldEntries.find { it.customField?.id == id }?.value, staticData = entries)
else -> data.append(dataSource.customFieldEntries.find { it.customField?.id == dataSource.id }?.numericValue, inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED)
}
InputFragment.buildAndShow(this, parent, data)
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" CustomFieldRow.NAME -> this.name = value as String? ?: ""
CustomFieldRow.TYPE -> this.type = (value as Type?)?.uniqueIdentifier ?: Type.LIST.uniqueIdentifier CustomFieldRow.TYPE -> this.type = (value as Type?)?.uniqueIdentifier ?: Type.LIST.uniqueIdentifier
CustomFieldRow.COPY_ON_DUPLICATE -> this.duplicateValue = value as Boolean? ?: false CustomFieldRow.COPY_ON_DUPLICATE -> this.duplicateValue = value as Boolean? ?: false
} }
@ -146,8 +167,9 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
} }
override fun isValidForDelete(realm: Realm): Boolean { override fun isValidForDelete(realm: Realm): Boolean {
val sessions = realm.where<Session>().contains("customFieldEntries.customField.id", id).findAll() return true
return sessions.isEmpty() // val sessions = realm.where<Session>().contains("customFieldEntries.customFields.id", id).findAll()
// return sessions.isEmpty()
} }
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int { override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
@ -155,60 +177,27 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
return R.string.cf_entry_delete_popup_message return R.string.cf_entry_delete_popup_message
} }
override val bottomSheetType: BottomSheetType override val inputFragmentType: InputFragmentType
get() { get() {
return when (type) { return when (type) {
Type.LIST.uniqueIdentifier -> BottomSheetType.LIST_STATIC Type.LIST.uniqueIdentifier -> InputFragmentType.LIST_STATIC
else -> BottomSheetType.NUMERIC_TEXT else -> InputFragmentType.NUMERIC_TEXT
} }
} }
override fun deleteDependencies() { override fun deleteDependencies(realm: Realm) {
if (isValid) { if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll() val entries = realm.where<CustomFieldEntry>().equalTo("customFields.id", id).findAll()
entries.deleteAllFromRealm() entries.deleteAllFromRealm()
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) {
is CustomFieldEntry -> row.editingDescriptors(
mapOf(
"defaultValue" to row.value
)
)
else -> null
}
}
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: Double? 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 * 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(CustomFieldRow.NAME)
rows.add(CustomFieldRow.TYPE) rows.add(CustomFieldRow.TYPE)
if (type == Type.LIST.uniqueIdentifier && entries.size >= 0) { if (type == Type.LIST.uniqueIdentifier && entries.size >= 0) {
@ -257,26 +246,24 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
* Delete an entry * Delete an entry
*/ */
fun deleteEntry(entry: CustomFieldEntry) { fun deleteEntry(entry: CustomFieldEntry) {
entries.remove(entry)
entriesToDelete.add(entry) entriesToDelete.add(entry)
entries.remove(entry)
sortEntries() sortEntries()
updateRowRepresentation() updateRowRepresentation()
} }
private fun removeListEntries() { fun cleanupEntries() { // called when saving the custom field
this.entriesToDelete.addAll(entries) val realm = Realm.getDefaultInstance()
this.entries.clear()
if (realm != null) { realm.executeTransaction {
realm.executeTransaction { this.entriesToDelete.forEach { // entries are out of realm
this.entriesToDelete.forEach { realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
if (it.isManaged) {
it.deleteFromRealm()
}
}
} }
} }
realm.close()
this.entriesToDelete.clear()
} }
/** /**

@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import android.text.InputType import android.text.InputType
import androidx.fragment.app.Fragment
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
@ -12,11 +13,14 @@ 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.exceptions.ModelException
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
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.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.input.InputFragment
import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
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.RowEditableDataSource
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 net.pokeranalytics.android.util.extensions.toCurrency
@ -27,6 +31,9 @@ import java.util.Currency
open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable { open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable {
@Ignore
override val realmObjectClass: Class<out Identifiable> = CustomFieldEntry::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
@ -77,7 +84,7 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable {
} }
@Ignore @Ignore
override val bottomSheetType: BottomSheetType = BottomSheetType.EDIT_TEXT override val inputFragmentType: InputFragmentType = InputFragmentType.EDIT_TEXT
override fun localizedTitle(context: Context): String { override fun localizedTitle(context: Context): String {
return context.getString(R.string.value) return context.getString(R.string.value)
@ -87,12 +94,16 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable {
return if (value.isNotEmpty()) value else NULL_TEXT return if (value.isNotEmpty()) value else NULL_TEXT
} }
override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowRepresentableEditDescriptor>? { override fun startEditing(dataSource: Any?, parent: Fragment?) {
val defaultValue: Any? by map if (parent == null) return
return arrayListOf( if (parent !is RowRepresentableDelegate) return
RowRepresentableEditDescriptor(defaultValue, R.string.value, InputType.TYPE_CLASS_TEXT) val data = RowEditableDataSource()
) data.append(this.value, R.string.value, InputType.TYPE_CLASS_TEXT)
} InputFragment.buildAndShow(this, parent, data, isDeletable = true)
}
override val valueCanBeClearedWhenEditing: Boolean
get() = false
override fun isValidForSave(): Boolean { override fun isValidForSave(): Boolean {
return true return true
@ -110,9 +121,9 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable {
return R.string.cf_entry_delete_popup_message return R.string.cf_entry_delete_popup_message
} }
override fun deleteDependencies() { override fun deleteDependencies(realm: Realm) {
if (isValid) { if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll() val entries = realm.where<Session>().contains("customFieldEntries.id", id).findAll()
entries.deleteAllFromRealm() entries.deleteAllFromRealm()
} }
} }

@ -1,21 +1,23 @@
package net.pokeranalytics.android.model.realm package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import androidx.fragment.app.Fragment
import io.realm.* import io.realm.*
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable 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.CountableUsage import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.ui.fragment.components.input.InputFragment
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.ImageDecorator import net.pokeranalytics.android.ui.view.*
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
import timber.log.Timber
import java.util.* import java.util.*
/** /**
@ -23,156 +25,197 @@ import java.util.*
* It contains a list of [FilterCondition] describing the complete query to launch * It contains a list of [FilterCondition] describing the complete query to launch
* The [Filter] is working closely with a [Filterable] interface providing the entity we want the query being launched on * The [Filter] is working closely with a [Filterable] interface providing the entity we want the query being launched on
*/ */
open class Filter : RealmObject(), RowRepresentable, Identifiable, Deletable, CountableUsage, ImageDecorator { open class Filter : RealmObject(), RowRepresentable, Editable, Deletable, CountableUsage, ImageDecorator {
@Ignore
override val realmObjectClass: Class<out Identifiable> = Filter::class.java
companion object { companion object {
// Create a new instance // Create a new instance
fun newInstance(filterableTypeUniqueIdentifier:Int): Filter { fun newInstance(filterableTypeUniqueIdentifier: Int): Filter {
val filter = Filter() val filter = Filter()
filter.filterableTypeUniqueIdentifier = filterableTypeUniqueIdentifier filter.filterableTypeUniqueIdentifier = filterableTypeUniqueIdentifier
return filter return filter
//return realm.copyToRealm(filter) //return realm.copyToRealm(filter)
} }
// Get a queryWith by its id
fun getFilterBydId(realm: Realm, filterId: String): Filter? {
return realm.where<Filter>().equalTo("id", filterId).findFirst()
}
inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> { inline fun <reified T : Filterable> queryOn(realm: Realm, query: Query, sortField: String? = null): RealmResults<T> {
val realmQuery = realm.where<T>() val rootQuery = realm.where<T>()
var realmQuery = query.queryWith(rootQuery)
sortField?.let { sortField?.let {
return query.queryWith(realmQuery).sort(it).findAll() realmQuery = realmQuery.sort(it)
} ?: run {
return query.queryWith(realmQuery).findAll()
} }
} // val desc = realmQuery.description
return realmQuery.findAll()
}
} }
override val viewType: Int
get() = RowViewType.TITLE_VALUE_ACTION.ordinal
override val imageRes: Int?
get() = R.drawable.ic_outline_settings
override val imageTint: Int?
get() = R.color.green
override val imageClickable: Boolean?
get() = true
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// the queryWith name // the queryWith name
var name: String = "" var name: String = ""
get() { get() {
if (field.isEmpty()) { if (field.isEmpty()) {
return this.query.defaultName return this.query.defaultName
} }
return field return field
} }
override var useCount: Int = 0 override var useCount: Int = 0
@Ignore
override val ownerClass: Class<out RealmModel> = Session::class.java
var filterConditions: RealmList<FilterCondition> = RealmList() var filterConditions: RealmList<FilterCondition> = RealmList()
private set private set
private var filterableTypeUniqueIdentifier: Int? = null private var filterableTypeUniqueIdentifier: Int? = null
val filterableType: FilterableType val filterableType: FilterableType
get() { get() {
this.filterableTypeUniqueIdentifier?.let { this.filterableTypeUniqueIdentifier?.let {
return FilterableType.valueByIdentifier(it) return FilterableType.valueByIdentifier(it)
} }
return FilterableType.ALL return FilterableType.ALL
} }
fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) { fun createOrUpdateFilterConditions(filterConditionRows: ArrayList<QueryCondition>) {
println("list of querys saving: ${filterConditionRows.map { it.id }}") Timber.d("list of querys saving: ${filterConditionRows.map { it.id }}")
println("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}") Timber.d("list of querys previous: ${this.filterConditions.map { it.queryCondition.id }}")
filterConditionRows filterConditionRows
.map { .map {
it.groupId it.groupId
} }
.distinct() .distinct()
.forEach { groupId-> .forEach { groupId ->
filterConditionRows filterConditionRows
.filter { .filter {
it.groupId == groupId it.groupId == groupId
} }
.apply { .apply {
println("list of querys: ${this.map { it.id }}") Timber.d("list of querys: ${this.map { it.id }}")
val casted = arrayListOf<QueryCondition>() val casted = arrayListOf<QueryCondition>()
casted.addAll(this) casted.addAll(this)
val newFilterCondition = FilterCondition(casted) val newFilterCondition = FilterCondition(casted)
val previousCondition = filterConditions.filter { val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
} }
filterConditions.removeAll(previousCondition) filterConditions.removeAll(previousCondition)
filterConditions.add(newFilterCondition) filterConditions.add(newFilterCondition)
} }
} }
} }
fun remove(filterCategoryRow: FilterCategoryRow) { fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name } val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) } val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections) this.filterConditions.removeAll(savedSections)
} }
fun countBy(filterCategoryRow: FilterCategoryRow): Int { fun countBy(filterCategoryRow: FilterCategoryRow): Int {
val sections = filterCategoryRow.filterSectionRows.map { it.name } val sections = filterCategoryRow.filterSectionRows.map { it.name }
println("list of sections $sections") Timber.d("list of sections $sections")
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id } val savedSections = filterConditions.filter { sections.contains(it.sectionName) }.flatMap { it.queryCondition.id }
println("list of savedSections $savedSections") Timber.d("list of savedSections $savedSections")
return savedSections.size return savedSections.size
} }
fun contains(filterElementRow: QueryCondition): Boolean { fun contains(filterElementRow: QueryCondition): Boolean {
println("list of saved queries ${filterConditions.map { it.queryCondition.id }}") Timber.d("list of saved queries ${filterConditions.map { it.queryCondition.id }}")
println("list of contains ${filterElementRow.id}") Timber.d("list of contains ${filterElementRow.id}")
val contained = filterConditions.flatMap{ it.queryCondition.id }.contains(filterElementRow.id.first()) val contained = filterConditions.flatMap { it.queryCondition.id }.contains(filterElementRow.id.first())
println("list of : $contained") Timber.d("list of : $contained")
return contained return contained
} }
/** /**
* Get the saved value for the given [filterElementRow] * Get the saved value for the given [filterElementRow]
*/ */
fun loadValueForElement(filterElementRow: QueryCondition) { fun loadValueForElement(filterElementRow: QueryCondition) {
val filtered = filterConditions.filter { val filtered = filterConditions.filter {
it.queryCondition.id == filterElementRow.id it.queryCondition.id == filterElementRow.id
} }
if (filtered.isNotEmpty()) { if (filtered.isNotEmpty()) {
return filterElementRow.updateValueBy(filtered.first()) return filterElementRow.updateValueBy(filtered.first())
} }
} }
inline fun <reified T : Filterable> results(firstField: String? = null, secondField: String? = null): RealmResults<T> { inline fun <reified T : Filterable> query(firstField: String? = null, secondField: String? = null): RealmQuery<T> {
val realmQuery = realm.where<T>()
val realmQuery = realm.where<T>() if (firstField != null && secondField != null) {
return this.query.queryWith(realmQuery).distinct(firstField, secondField)
}
if (firstField != null && secondField != null) { if (firstField != null) {
return this.query.queryWith(realmQuery).distinct(firstField, secondField).findAll() return this.query.queryWith(realmQuery).distinct(firstField)
} }
if (firstField != null) { return this.query.queryWith(realmQuery)
return this.query.queryWith(realmQuery).distinct(firstField).findAll() }
}
return this.query.queryWith(realmQuery).findAll() inline fun <reified T : Filterable> results(firstField: String? = null, secondField: String? = null): RealmResults<T> {
} return this.query<T>(firstField, secondField).findAll()
}
val query: Query val query: Query
get() { get() {
val query = Query() val query = Query()
this.filterConditions.forEach { this.filterConditions.forEach {
query.add(it.queryCondition) query.add(it.queryCondition)
} }
return query return query
} }
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
if (name.isNotEmpty()) return name if (name.isNotEmpty()) return name
return this.query.getName(context) return this.query.getName(context)
} }
override fun isValidForDelete(realm: Realm): Boolean { override fun isValidForDelete(realm: Realm): Boolean {
return true return true
} }
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int { override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.relationship_error return R.string.relationship_error
} }
override val inputFragmentType: InputFragmentType
get() {
return InputFragmentType.EDIT_TEXT
}
override fun localizedTitle(context: Context): String {
return context.getString(R.string.name)
}
override fun updateValue(value: Any?, row: RowRepresentable) {
realm.executeTransaction {
val newName = value as String? ?: ""
if (newName.isNotEmpty()) {
name = newName
}
}
}
override fun startEditing(dataSource: Any?, parent: Fragment?) {
if (parent == null) return
if (parent !is RowRepresentableDelegate) return
val data = RowEditableDataSource()
data.append(this.name)
InputFragment.buildAndShow(this, parent, data, isDeletable = true, valueHasPlaceholder = false)
}
} }

@ -2,28 +2,32 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.CountableUsage import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.interfaces.Identifiable
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.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.GameRow import net.pokeranalytics.android.ui.view.rowrepresentable.GameRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class Game : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable, CountableUsage { open class Game : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable, CountableUsage {
@Ignore
override val realmObjectClass: Class<out Identifiable> = Game::class.java
companion object { companion object {
val rowRepresentation : List<RowRepresentable> by lazy { val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME) rows.add(GameRow.NAME)
// rows.addAll(GameRow.values()) // rows.addAll(GameRow.values())
rows rows
} }
@ -41,6 +45,9 @@ open class Game : RealmObject(), NameManageable, StaticRowRepresentableDataSourc
// CountableUsage // CountableUsage
override var useCount: Int = 0 override var useCount: Int = 0
@Ignore
override val ownerClass: Class<out RealmModel> = Session::class.java
fun getNotNullShortName() : String { fun getNotNullShortName() : String {
this.shortName?.let { this.shortName?.let {
return it return it
@ -53,28 +60,20 @@ open class Game : RealmObject(), NameManageable, StaticRowRepresentableDataSourc
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return Game.rowRepresentation return rowRepresentation
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT GameRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT
GameRow.SHORT_NAME -> this.shortName ?: NULL_TEXT GameRow.SHORT_NAME -> this.shortName ?: NULL_TEXT
else -> return super.stringForRow(row) else -> return super.stringForRow(row)
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) {
SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.name))
GameRow.SHORT_NAME -> row.editingDescriptors(mapOf("defaultValue" to this.shortName))
else -> null
}
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" GameRow.NAME -> this.name = value as String? ?: ""
GameRow.SHORT_NAME -> this.shortName = value as String? ?: "" GameRow.SHORT_NAME -> this.shortName = value as String? ?: ""
} }
} }

@ -4,9 +4,11 @@ import android.content.Context
import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.api.model.Place
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Identifiable
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.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
@ -16,6 +18,9 @@ import java.util.*
open class Location : RealmObject(), NameManageable, RowRepresentable { open class Location : RealmObject(), NameManageable, RowRepresentable {
@Ignore
override val realmObjectClass: Class<out Identifiable> = Location::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()

@ -11,6 +11,7 @@ import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.Criteria import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.findById import net.pokeranalytics.android.util.extensions.findById
@ -19,6 +20,9 @@ import java.util.*
open class ReportSetup : RealmObject(), RowRepresentable, Deletable { open class ReportSetup : RealmObject(), RowRepresentable, Deletable {
@Ignore
override val realmObjectClass: Class<out Identifiable> = ReportSetup::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()

@ -6,6 +6,7 @@ import io.realm.RealmResults
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import io.realm.annotations.RealmClass import io.realm.annotations.RealmClass
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition
@ -52,10 +53,10 @@ open class Result : RealmObject(), Filterable {
this.session?.bankroll?.let { bankroll -> this.session?.bankroll?.let { bankroll ->
if (bankroll.live) { if (bankroll.live) {
throw IllegalStateException("Can't set net result on a live bankroll") throw PAIllegalStateException("Can't set net result on a live bankroll")
} }
} ?: run { } ?: run {
throw IllegalStateException("Session doesn't have any bankroll") throw PAIllegalStateException("Session doesn't have any bankroll")
} }
field = value field = value

@ -16,6 +16,7 @@ import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.StatFormattingException import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.TableSize import net.pokeranalytics.android.model.TableSize
import net.pokeranalytics.android.model.TournamentType import net.pokeranalytics.android.model.TournamentType
@ -29,7 +30,6 @@ import net.pokeranalytics.android.model.utils.SessionSetManager
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException
import net.pokeranalytics.android.ui.fragment.GraphFragment import net.pokeranalytics.android.ui.fragment.GraphFragment
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import net.pokeranalytics.android.ui.view.* import net.pokeranalytics.android.ui.view.*
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRow import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRow
@ -46,7 +46,7 @@ import kotlin.collections.ArrayList
typealias BB = Double typealias BB = Double
open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDataSource, RowRepresentable, Timed, open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDataSource, RowRepresentable, Timed,
TimeFilterable, Filterable, DatedValue { TimeFilterable, Filterable, DatedBankrollGraphEntry {
enum class Type { enum class Type {
CASH_GAME, CASH_GAME,
@ -67,7 +67,8 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
companion object { companion object {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null, managed: Boolean = true): Session {
val session = Session() val session = Session()
session.result = Result() session.result = Result()
if (bankroll != null) { if (bankroll != null) {
@ -76,7 +77,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
session.bankroll = realm.where<Bankroll>().findFirst() session.bankroll = realm.where<Bankroll>().findFirst()
} }
session.type = if (isTournament) Session.Type.TOURNAMENT.ordinal else Session.Type.CASH_GAME.ordinal session.type = if (isTournament) Session.Type.TOURNAMENT.ordinal else Session.Type.CASH_GAME.ordinal
return realm.copyToRealm(session)
return if (managed) {
realm.copyToRealm(session)
} else {
session
}
} }
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? { fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? {
@ -235,9 +241,10 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
var creationDate: Date = Date() var creationDate: Date = Date()
// The bankroll hosting the results // The bankroll hosting the results
var bankroll: Bankroll? = null override var bankroll: Bankroll? = null
set(value) { set(value) {
field = value field = value
this.formatBlinds()
this.updateRowRepresentation() this.updateRowRepresentation()
} }
@ -399,7 +406,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
@Ignore @Ignore
override var amount: Double = 0.0 override var amount: Double = 0.0
get() { get() {
return this.computableResult?.ratedNet ?: 0.0 return this.result?.net ?: 0.0
} }
/** /**
@ -420,7 +427,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
this.computableResults?.forEachIndexed { index, computableResult -> this.computableResults?.forEachIndexed { index, computableResult ->
computableResult.updateWith(this) computableResult.updateWith(this)
if (index > 0) { if (index > 0) {
throw IllegalStateException("Session cannot have more than one computable result") throw PAIllegalStateException("Session cannot have more than one computable result")
} }
} }
this.sessionSet?.computeStats() this.sessionSet?.computeStats()
@ -498,12 +505,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
if (pauseDate != null) { if (pauseDate != null) {
this.breakDuration += Date().time - pauseDate.time this.breakDuration += Date().time - pauseDate.time
} else { } else {
throw IllegalStateException("When resuming, the pause date must be set") throw PAIllegalStateException("When resuming, the pause date must be set")
} }
this.pauseDate = null this.pauseDate = null
} }
else -> { else -> {
throw IllegalStateException("unmanaged session state") throw PAIllegalStateException("unmanaged session state")
} }
} }
} }
@ -524,7 +531,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
SessionState.STARTED -> { SessionState.STARTED -> {
this.pauseDate = Date() this.pauseDate = Date()
} }
else -> throw IllegalStateException("Pausing a session in an unmanaged state") else -> throw PAIllegalStateException("Pausing a session in an unmanaged state")
} }
} }
} }
@ -613,7 +620,12 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
if (cgBigBlind == null) return if (cgBigBlind == null) return
cgBigBlind?.let { bb -> cgBigBlind?.let { bb ->
val sb = cgSmallBlind ?: bb / 2.0 val sb = cgSmallBlind ?: bb / 2.0
blinds = "${currency.symbol} ${sb.formatted()}/${bb.round()}" val preFormattedBlinds = "${sb.formatted()}/${bb.round()}"
println("<<<<<< bb.toCurrency(currency) : ${bb.toCurrency(currency)}")
println("<<<<<< preFormattedBlinds : $preFormattedBlinds")
val regex = Regex("-?\\d+(\\.\\d+)?")
blinds = bb.toCurrency(currency).replace(regex, preFormattedBlinds)
println("<<<<<< blinds = $blinds")
} }
} }
@ -646,6 +658,24 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
fun duplicate() : Session {
val copy = Session.newInstance(this.realm, this.isTournament(), this.bankroll)
copy.game = this.game
copy.limit = this.limit
copy.cgSmallBlind = this.cgSmallBlind
copy.cgBigBlind = this.cgBigBlind
copy.tournamentEntryFee = this.tournamentEntryFee
copy.tournamentFeatures = this.tournamentFeatures
copy.tournamentName = this.tournamentName
copy.tournamentType = this.tournamentType
copy.tableSize = this.tableSize
copy.numberOfTables = this.numberOfTables
return copy
}
@Ignore @Ignore
override val viewType: Int = RowViewType.ROW_SESSION.ordinal override val viewType: Int = RowViewType.ROW_SESSION.ordinal
@ -791,7 +821,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
return NULL_TEXT return NULL_TEXT
} }
else -> throw UnmanagedRowRepresentableException("Unmanaged row = ${row}") else -> throw UnmanagedRowRepresentableException("Unmanaged row = $row")
} }
} }
@ -804,117 +834,6 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) {
SessionRow.BANKROLL -> row.editingDescriptors(
mapOf(
"defaultValue" to this.bankroll,
"data" to realm.sorted<Bankroll>() // LiveData.Bankroll.items(realm)
)
)
SessionRow.GAME -> row.editingDescriptors(
mapOf(
"limit" to this.limit,
"defaultValue" to this.game,
"data" to realm.sorted<Game>() //LiveData.Game.items(realm)
)
)
SessionRow.LOCATION -> row.editingDescriptors(
mapOf(
"defaultValue" to this.location,
"data" to realm.sorted<Location>() // LiveData.Location.items(realm)
)
)
SessionRow.TOURNAMENT_FEATURE -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tournamentFeatures,
"data" to realm.sorted<TournamentFeature>() //LiveData.TournamentFeature.items(realm)
)
)
SessionRow.TOURNAMENT_NAME -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tournamentName,
"data" to realm.sorted<TournamentName>() //LiveData.TournamentName.items(realm)
)
)
SessionRow.TOURNAMENT_TYPE -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tournamentType
)
)
SessionRow.TABLE_SIZE -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tableSize
)
)
SessionRow.BLINDS -> row.editingDescriptors(
mapOf(
"sb" to cgSmallBlind?.round(),
"bb" to cgBigBlind?.round()
)
)
SessionRow.BUY_IN -> row.editingDescriptors(
mapOf(
"bb" to cgBigBlind,
"fee" to this.tournamentEntryFee,
"ratedBuyin" to result?.buyin
)
)
SessionRow.BREAK_TIME -> row.editingDescriptors(mapOf())
SessionRow.CASHED_OUT, SessionRow.PRIZE -> row.editingDescriptors(
mapOf(
"defaultValue" to result?.cashout
)
)
SessionRow.NET_RESULT -> row.editingDescriptors(
mapOf(
"defaultValue" to result?.netResult
)
)
SessionRow.COMMENT -> row.editingDescriptors(
mapOf(
"defaultValue" to this.comment
)
)
SessionRow.INITIAL_BUY_IN -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tournamentEntryFee
)
)
SessionRow.PLAYERS -> row.editingDescriptors(
mapOf(
"defaultValue" to this.tournamentNumberOfPlayers
)
)
SessionRow.POSITION -> row.editingDescriptors(
mapOf(
"defaultValue" to this.result?.tournamentFinalPosition
)
)
SessionRow.TIPS -> row.editingDescriptors(
mapOf(
"sb" to cgSmallBlind?.round(),
"bb" to cgBigBlind?.round(),
"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 }?.numericValue
)
}
)
}
else -> null
}
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
realm.executeTransaction { realm.executeTransaction {
@ -1098,7 +1017,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
} ?: run { } ?: run {
throw java.lang.IllegalStateException("Asking for statIds on Session without Result") throw PAIllegalStateException("Asking for statIds on Session without Result")
} }
} }
@ -1138,7 +1057,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
DefaultLegendValues(this.entryTitle(context), left, right) DefaultLegendValues(this.entryTitle(context), left, right)
} }
else -> { else -> {
super.legendValues(stat, entry, style, groupName, context) super<Timed>.legendValues(stat, entry, style, groupName, context)
} }
} }
} }
@ -1147,10 +1066,7 @@ open class Session : RealmObject(), Savable, Editable, StaticRowRepresentableDat
} }
// Timed @Ignore
override val realmObjectClass: Class<out Identifiable> = Session::class.java
override val objectIdentifier: ObjectIdentifier
get() = ObjectIdentifier(this.id, Session::class.java)
} }

@ -4,16 +4,17 @@ import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.StatFormattingException import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.util.TextFormat
import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.Filterable
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.Timed import net.pokeranalytics.android.model.interfaces.Timed
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.TextFormat
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
@ -138,10 +139,8 @@ open class SessionSet() : RealmObject(), Timed, Filterable {
} }
} }
// Timed @Ignore
override val realmObjectClass: Class<out Identifiable> = SessionSet::class.java
override val objectIdentifier: ObjectIdentifier
get() = ObjectIdentifier(this.id, SessionSet::class.java)
} }

@ -233,7 +233,7 @@
// //
// /** // /**
// * Multiple session sets update: // * Multiple session sets update:
// * Merges all sets into one (delete all then create a new one) // * Merges all sets into one (delete all then buildAndShow a new one)
// */ // */
// private fun mergeSessionGroups(owner: Session, sessionSets: RealmResults<SessionSet>) { // private fun mergeSessionGroups(owner: Session, sessionSets: RealmResults<SessionSet>) {
// //

@ -2,17 +2,18 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.CountableUsage import net.pokeranalytics.android.model.interfaces.CountableUsage
import net.pokeranalytics.android.model.interfaces.Identifiable
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.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.ui.view.rowrepresentable.TournamentFeatureRow import net.pokeranalytics.android.ui.view.rowrepresentable.TournamentFeatureRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import java.util.* import java.util.*
@ -24,12 +25,14 @@ open class TournamentFeature : RealmObject(), NameManageable, StaticRowRepresent
companion object { companion object {
val rowRepresentation : List<RowRepresentable> by lazy { val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TournamentFeatureRow.values()) rows.addAll(TournamentFeatureRow.values())
rows rows
} }
} }
@Ignore
override val realmObjectClass: Class<out Identifiable> = TournamentFeature::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
@ -39,29 +42,27 @@ open class TournamentFeature : RealmObject(), NameManageable, StaticRowRepresent
// CountableUsage // CountableUsage
override var useCount: Int = 0 override var useCount: Int = 0
@Ignore
override val ownerClass: Class<out RealmModel> = Session::class.java
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
return this.name return this.name
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return TournamentFeature.rowRepresentation return rowRepresentation
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT TournamentFeatureRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT
else -> return super.stringForRow(row) else -> return super.stringForRow(row)
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf(
"defaultValue" to this.name))
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" TournamentFeatureRow.NAME -> this.name = value as String? ?: ""
} }
} }

@ -3,15 +3,15 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Identifiable
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.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.ui.view.rowrepresentable.TournamentNameRow import net.pokeranalytics.android.ui.view.rowrepresentable.TournamentNameRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import java.util.* import java.util.*
@ -22,12 +22,14 @@ open class TournamentName : RealmObject(), NameManageable, StaticRowRepresentabl
companion object { companion object {
val rowRepresentation : List<RowRepresentable> by lazy { val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TournamentNameRow.values()) rows.addAll(TournamentNameRow.values())
rows rows
} }
} }
@Ignore
override val realmObjectClass: Class<out Identifiable> = TournamentName::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
@ -40,25 +42,21 @@ open class TournamentName : RealmObject(), NameManageable, StaticRowRepresentabl
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" TournamentNameRow.NAME -> this.name = value as String? ?: ""
} }
} }
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return TournamentName.rowRepresentation return rowRepresentation
} }
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT TournamentNameRow.NAME -> if (this.name.isNotEmpty()) this.name else NULL_TEXT
else -> return super.stringForRow(row) else -> return super.stringForRow(row)
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf("defaultValue" to this.name))
}
override fun getFailedSaveMessage(status: SaveValidityStatus): Int { override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
return when (status) { return when (status) {
SaveValidityStatus.DATA_INVALID -> R.string.tournament_name_empty_field_error SaveValidityStatus.DATA_INVALID -> R.string.tournament_name_empty_field_error

@ -13,7 +13,6 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.* import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.GraphFragment import net.pokeranalytics.android.ui.fragment.GraphFragment
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.view.DefaultLegendValues import net.pokeranalytics.android.ui.view.DefaultLegendValues
import net.pokeranalytics.android.ui.view.LegendContent import net.pokeranalytics.android.ui.view.LegendContent
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
@ -26,11 +25,22 @@ import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, TimeFilterable, Filterable, DatedValue, open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, TimeFilterable,
GraphUnderlyingEntry { Filterable, DatedBankrollGraphEntry {
companion object { companion object {
fun newInstance(realm: Realm, bankroll: Bankroll, date: Date? = null, type: TransactionType, amount: Double): Transaction {
val transaction = realm.copyToRealm(Transaction())
transaction.date = date ?: Date()
transaction.amount = amount
transaction.type = type
transaction.bankroll = bankroll
return transaction
}
val rowRepresentation: List<RowRepresentable> by lazy { val rowRepresentation: List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.addAll(TransactionRow.values()) rows.addAll(TransactionRow.values())
@ -60,7 +70,7 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
// The bankroll of the transaction // The bankroll of the transaction
var bankroll: Bankroll? = null override var bankroll: Bankroll? = null
// The amount of the transaction // The amount of the transaction
override var amount: Double = 0.0 override var amount: Double = 0.0
@ -134,14 +144,17 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return SaveValidityStatus.VALID return SaveValidityStatus.VALID
} }
// GraphUnderlyingEntry // GraphIdentifiableEntry
@Ignore
override val realmObjectClass: Class<out Identifiable> = Transaction::class.java
override fun entryTitle(context: Context): String { override fun entryTitle(context: Context): String {
return DateFormat.getDateInstance(DateFormat.SHORT).format(this.date) return DateFormat.getDateInstance(DateFormat.SHORT).format(this.date)
} }
override fun formattedValue(stat: Stat): TextFormat { override fun formattedValue(stat: Stat): TextFormat {
return stat.format(this.amount) return stat.format(this.amount, currency = this.bankroll?.utilCurrency)
} }
override fun legendValues( override fun legendValues(
@ -158,4 +171,5 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return DefaultLegendValues(this.entryTitle(context), entryValue, totalStatValue, leftName = leftName) return DefaultLegendValues(this.entryTitle(context), entryValue, totalStatValue, leftName = leftName)
} }
} }

@ -2,27 +2,40 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.Localizable import net.pokeranalytics.android.ui.view.Localizable
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.rowrepresentable.SimpleRow
import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionTypeRow import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionTypeRow
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import net.pokeranalytics.android.util.enumerations.IntSearchable
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable,
CountableUsage {
enum class Value(val additive: Boolean) : Localizable { enum class Value(override var uniqueIdentifier: Int, val additive: Boolean) : IntIdentifiable, Localizable {
WITHDRAWAL(false),
DEPOSIT(true), WITHDRAWAL(0, false),
BONUS(true); DEPOSIT(1, true),
BONUS(2, true),
STACKING_INCOMING(3, true),
STACKING_OUTGOING(4, false);
companion object : IntSearchable<Value> {
override fun valuesInternal(): Array<Value> {
return values()
}
}
override val resId: Int? override val resId: Int?
get() { get() {
@ -30,34 +43,39 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
WITHDRAWAL -> R.string.withdrawal WITHDRAWAL -> R.string.withdrawal
DEPOSIT -> R.string.deposit DEPOSIT -> R.string.deposit
BONUS -> R.string.bonus BONUS -> R.string.bonus
STACKING_INCOMING -> R.string.stacking_incoming
STACKING_OUTGOING -> R.string.stacking_outgoing
} }
} }
} }
companion object { companion object {
val rowRepresentation: List<RowRepresentable> by lazy { val rowRepresentation: List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>() val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TransactionTypeRow.values()) rows.addAll(TransactionTypeRow.values())
rows rows
} }
fun getByValue(value: Value, realm: Realm): TransactionType { fun getByValue(value: Value, realm: Realm): TransactionType {
val type = realm.where(TransactionType::class.java).equalTo("kind", value.ordinal).findFirst() val type = realm.where(TransactionType::class.java).equalTo("kind", value.uniqueIdentifier).findFirst()
type?.let { type?.let {
return it return it
} }
throw IllegalStateException("Transaction type ${value.name} should exist in database!") throw PAIllegalStateException("Transaction type ${value.name} should exist in database!")
} }
} }
@Ignore
override val realmObjectClass: Class<out Identifiable> = TransactionType::class.java
@PrimaryKey @PrimaryKey
override var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
/** /**
* The name of the transaction type * The name of the transaction type
*/ */
override var name: String = "" override var name: String = ""
/** /**
@ -72,6 +90,11 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
// The predefined kind, if necessary, like: Withdrawal, deposit, or tips // The predefined kind, if necessary, like: Withdrawal, deposit, or tips
var kind: Int? = null var kind: Int? = null
override var useCount: Int = 0
@Ignore
override val ownerClass: Class<out RealmModel> = Transaction::class.java
override fun getDisplayName(context: Context): String { override fun getDisplayName(context: Context): String {
return this.name return this.name
} }
@ -82,7 +105,7 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> this.name TransactionTypeRow.NAME -> this.name
else -> return super.stringForRow(row) else -> return super.stringForRow(row)
} }
} }
@ -94,13 +117,9 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf("defaultValue" to this.name))
}
override fun updateValue(value: Any?, row: RowRepresentable) { override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
SimpleRow.NAME -> this.name = value as String? ?: "" TransactionTypeRow.NAME -> this.name = value as String? ?: ""
TransactionTypeRow.TRANSACTION_ADDITIVE -> this.additive = value as Boolean? ?: false TransactionTypeRow.TRANSACTION_ADDITIVE -> this.additive = value as Boolean? ?: false
} }
} }
@ -114,5 +133,13 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
return R.string.transaction_relationship_error return R.string.transaction_relationship_error
} }
override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
return when (status) {
SaveValidityStatus.DATA_INVALID -> R.string.operation_type_empty_field_error
SaveValidityStatus.ALREADY_EXISTS -> R.string.duplicate_operation_type_error
else -> super.getFailedSaveMessage(status)
}
}
} }

@ -0,0 +1,39 @@
package net.pokeranalytics.android.model.utils
import io.realm.Realm
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
import java.util.*
class DataUtils {
companion object {
/**
* Returns true if the provided parameters doesn't correspond to an existing session
*/
fun sessionCount(realm: Realm, startDate: Date, endDate: Date, net: Double): Int {
val sessions = realm.where(Session::class.java)
.equalTo("startDate", startDate)
.equalTo("endDate", endDate)
.equalTo("result.net", net)
.findAll()
return sessions.size
}
/**
* Returns true if the provided parameters doesn't correspond to an existing transaction
*/
fun transactionUnicityCheck(realm: Realm, date: Date, amount: Double, type: TransactionType): Boolean {
val transactions = realm.where(Transaction::class.java)
.equalTo("date", date)
.equalTo("amount", amount)
.equalTo("type.id", type.id)
.findAll()
return transactions.isEmpty()
}
}
}

@ -71,27 +71,27 @@ class FavoriteSessionFinder {
private const val FAVORITE_SIGNIFICANT_SESSIONS = 15L private const val FAVORITE_SIGNIFICANT_SESSIONS = 15L
/** /**
* Copies the favorite session parameters on the [newSession] * Copies the favorite session parameters on the [session]
*/ */
fun copyParametersFromFavoriteSession(newSession: Session, location: Location?, context: Context) { fun copyParametersFromFavoriteSession(session: Session, location: Location?, context: Context) {
val favoriteSession = val favoriteSession =
favoriteSession(newSession.type, location, newSession.realm, context) favoriteSession(session.type, location, session.realm, context)
favoriteSession?.let { fav -> favoriteSession?.let { fav ->
newSession.limit = fav.limit session.limit = fav.limit
newSession.game = fav.game session.game = fav.game
newSession.bankroll = fav.bankroll session.bankroll = fav.bankroll
newSession.tableSize = fav.tableSize session.tableSize = fav.tableSize
when (newSession.type) { when (session.type) {
Session.Type.CASH_GAME.ordinal -> { Session.Type.CASH_GAME.ordinal -> {
newSession.cgSmallBlind = fav.cgSmallBlind session.cgSmallBlind = fav.cgSmallBlind
newSession.cgBigBlind = fav.cgBigBlind session.cgBigBlind = fav.cgBigBlind
} }
Session.Type.TOURNAMENT.ordinal -> { Session.Type.TOURNAMENT.ordinal -> {
newSession.tournamentEntryFee = fav.tournamentEntryFee session.tournamentEntryFee = fav.tournamentEntryFee
} }
} }
} }

@ -12,11 +12,29 @@ import java.util.*
class Seed(var context:Context) : Realm.Transaction { class Seed(var context:Context) : Realm.Transaction {
companion object {
fun createDefaultTransactionTypes(values: Array<TransactionType.Value>, context: Context, realm: Realm) {
values.forEach { value ->
val existing = realm.where(TransactionType::class.java).equalTo("kind", value.uniqueIdentifier).findAll()
if (existing.isEmpty()) {
val type = TransactionType()
type.name = value.localizedTitle(context)
type.additive = value.additive
type.kind = value.uniqueIdentifier
type.lock = true
realm.insertOrUpdate(type)
}
}
}
}
override fun execute(realm: Realm) { override fun execute(realm: Realm) {
this.createDefaultGames(realm) this.createDefaultGames(realm)
this.createDefaultTournamentFeatures(realm) this.createDefaultTournamentFeatures(realm)
this.createDefaultCurrencyAndBankroll(realm) this.createDefaultCurrencyAndBankroll(realm)
this.createDefaultTransactionTypes(realm) createDefaultTransactionTypes(TransactionType.Value.values(), context, realm)
} }
private fun createDefaultTournamentFeatures(realm: Realm) { private fun createDefaultTournamentFeatures(realm: Realm) {
@ -56,15 +74,4 @@ class Seed(var context:Context) : Realm.Transaction {
} }
} }
private fun createDefaultTransactionTypes(realm: Realm) {
TransactionType.Value.values().forEachIndexed { index, value ->
val type = TransactionType()
type.name = value.localizedTitle(context)
type.additive = value.additive
type.kind = index
type.lock = true
realm.insertOrUpdate(type)
}
}
} }

@ -3,6 +3,7 @@ package net.pokeranalytics.android.model.utils
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.RealmResults import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.ModelException import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet import net.pokeranalytics.android.model.realm.SessionSet
import kotlin.math.max import kotlin.math.max
@ -23,7 +24,7 @@ class SessionSetManager {
fun updateTimeline(session: Session) { fun updateTimeline(session: Session) {
if (!session.realm.isInTransaction) { if (!session.realm.isInTransaction) {
throw IllegalStateException("realm should be in transaction at this point") throw PAIllegalStateException("realm should be in transaction at this point")
} }
if (session.startDate == null) { if (session.startDate == null) {
@ -96,7 +97,7 @@ class SessionSetManager {
/** /**
* Multiple session sets update: * Multiple session sets update:
* Merges all sets into one (delete all then create a new one) * Merges all sets into one (delete all then buildAndShow a new one)
*/ */
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) { private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) {
@ -154,7 +155,7 @@ class SessionSetManager {
fun removeFromTimeline(session: Session) { fun removeFromTimeline(session: Session) {
if (!session.realm.isInTransaction) { if (!session.realm.isInTransaction) {
throw IllegalStateException("realm should be in transaction at this point") throw PAIllegalStateException("realm should be in transaction at this point")
} }
val sessionSet = session.sessionSet val sessionSet = session.sessionSet

@ -1,21 +0,0 @@
package net.pokeranalytics.android.model.utils
import io.realm.Realm
import net.pokeranalytics.android.model.realm.Session
import java.util.*
class SessionUtils {
companion object {
/**
* Returns true if the provided parameters doesn't correspond to an existing session
*/
fun unicityCheck(realm: Realm, startDate: Date, endDate: Date, net: Double) : Boolean {
val sessions = realm.where(Session::class.java).equalTo("startDate", startDate).equalTo("endDate", endDate).equalTo("result.net", net).findAll()
return sessions.isEmpty()
}
}
}

@ -37,54 +37,6 @@ class BankrollActivity : PokerAnalyticsActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bankroll) setContentView(R.layout.activity_bankroll)
// this.computableResults = getRealm().where(ComputableResult::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.computableResults.addChangeListener { t, changeSet ->
//
// val bankrolls = mutableSetOf<Bankroll>()
// val indexes = mutableSetOf<Int>()
// indexes.addAll(changeSet.changes.toList())
// indexes.addAll(changeSet.insertions.toList())
// indexes.addAll(changeSet.deletions.toList())
// indexes.forEach { index ->
// t[index]?.session?.bankroll?.let { br ->
// bankrolls.add(br)
// }
// }
// this.computeBankrollReports(bankrolls)
// }
// this.bankrolls = getRealm().where(Bankroll::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.bankrolls.addChangeListener { _, changeSet ->
//
//
//
//
//
// }
// this.transactions = getRealm().where(Transaction::class.java).findAll() // ComputableResult are existing only if sessions are ended
// this.transactions.addChangeListener { t, changeSet ->
//
// val bankrolls = mutableSetOf<Bankroll>()
// val indexes = mutableSetOf<Int>()
// indexes.addAll(changeSet.changes.toList())
// indexes.addAll(changeSet.insertions.toList())
// indexes.addAll(changeSet.deletions.toList())
// indexes.forEach { index ->
// if (t.isNotEmpty()) {
// t[index]?.bankroll?.let { br ->
// bankrolls.add(br)
// }
// }
// }
// this.computeBankrollReports(bankrolls)
// }
}
fun computeBankrollReports(bankrolls: Collection<Bankroll>) {
} }
} }

@ -1,28 +1,52 @@
package net.pokeranalytics.android.ui.activity package net.pokeranalytics.android.ui.activity
import android.content.Context import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.fragment.SubscriptionFragment
class BillingActivity : PokerAnalyticsActivity() { class BillingActivity : PokerAnalyticsActivity() {
private enum class IntentKey(val keyName: String) {
SHOW_MESSAGE("showMessage"),
}
companion object { companion object {
fun newInstance(context: Context) {
val intent = Intent(context, BillingActivity::class.java) fun newInstanceForResult(activity: Activity, showSessionMessage: Boolean) {
context.startActivity(intent) val intent = Intent(activity, BillingActivity::class.java)
intent.putExtra(IntentKey.SHOW_MESSAGE.keyName, showSessionMessage)
activity.startActivityForResult(intent, RequestCode.SUBSCRIPTION.value)
}
fun newInstanceForResult(fragment: Fragment, showSessionMessage: Boolean) {
val intent = Intent(fragment.requireContext(), BillingActivity::class.java)
intent.putExtra(IntentKey.SHOW_MESSAGE.keyName, showSessionMessage)
fragment.startActivityForResult(intent, RequestCode.SUBSCRIPTION.value)
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_billing) setContentView(R.layout.activity_billing)
initUI()
} }
override fun onResume() { private fun initUI() {
super.onResume()
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = SubscriptionFragment()
val showSessionMessage = intent.getBooleanExtra(IntentKey.SHOW_MESSAGE.keyName, false)
fragmentTransaction.add(R.id.container, fragment)
fragmentTransaction.commit()
fragment.setData(showSessionMessage)
} }

@ -67,10 +67,10 @@ class EditableDataActivity : PokerAnalyticsActivity() {
LiveData.TRANSACTION_TYPE.ordinal -> TransactionTypeDataFragment() LiveData.TRANSACTION_TYPE.ordinal -> TransactionTypeDataFragment()
else -> EditableDataFragment() else -> EditableDataFragment()
} }
fragment.setData(dataType, primaryKey)
fragmentTransaction.add(R.id.container, fragment) fragmentTransaction.add(R.id.container, fragment)
fragmentTransaction.commit() fragmentTransaction.commit()
fragment.setData(dataType, primaryKey)
} }
} }

@ -64,9 +64,9 @@ class FiltersActivity : PokerAnalyticsActivity() {
val filterableType = FilterableType.valueByIdentifier(uniqueIdentifier) val filterableType = FilterableType.valueByIdentifier(uniqueIdentifier)
fragment = FiltersFragment() fragment = FiltersFragment()
fragment.setData(filterId, filterableType)
fragmentTransaction.add(R.id.container, fragment) fragmentTransaction.add(R.id.container, fragment)
fragmentTransaction.commit() fragmentTransaction.commit()
fragment.setData(filterId, filterableType)
fragment.updateMostUsedFiltersVisibility(!hideMostUsedFilters) fragment.updateMostUsedFiltersVisibility(!hideMostUsedFilters)
} }

@ -0,0 +1,58 @@
package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.activity_filters_list.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.fragment.FiltersListFragment
import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode
class FiltersListActivity : PokerAnalyticsActivity() {
enum class IntentKey(val keyName: String) {
DATA_TYPE("DATA_TYPE"),
LIVE_DATA_TYPE("LIVE_DATA_TYPE"),
ITEM_DELETED("ITEM_DELETED"),
SHOW_ADD_BUTTON("SHOW_ADD_BUTTON"),
}
companion object {
fun newInstance(context: Context, dataType: Int) {
context.startActivity(getIntent(context, dataType))
}
fun newSelectInstance(fragment: Fragment, dataType: Int, showAddButton: Boolean = true) {
val context = fragment.requireContext()
fragment.startActivityForResult(getIntent(context, dataType, showAddButton), FilterActivityRequestCode.SELECT_FILTER.ordinal)
}
private fun getIntent(context: Context, dataType: Int, showAddButton: Boolean = true): Intent {
val intent = Intent(context, FiltersListActivity::class.java)
intent.putExtra(IntentKey.DATA_TYPE.keyName, dataType)
intent.putExtra(IntentKey.SHOW_ADD_BUTTON.keyName, showAddButton)
return intent
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_filters_list)
initUI()
}
/**
* Init UI
*/
private fun initUI() {
val dataType = intent.getIntExtra(IntentKey.DATA_TYPE.keyName, 0)
val showAddButton = intent.getBooleanExtra(IntentKey.SHOW_ADD_BUTTON.keyName, true)
val fragment = filtersListFragment as FiltersListFragment
fragment.setData(dataType)
fragment.updateUI(showAddButton)
}
}

@ -3,7 +3,6 @@ package net.pokeranalytics.android.ui.activity
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
@ -13,12 +12,8 @@ import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Currency import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.activity.components.ResultCode
import net.pokeranalytics.android.ui.adapter.HomePagerAdapter import net.pokeranalytics.android.ui.adapter.HomePagerAdapter
import net.pokeranalytics.android.ui.extensions.showAlertDialog
import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.billing.AppGuard
import timber.log.Timber
class HomeActivity : PokerAnalyticsActivity() { class HomeActivity : PokerAnalyticsActivity() {
@ -48,7 +43,7 @@ class HomeActivity : PokerAnalyticsActivity() {
R.id.navigation_reports -> { R.id.navigation_reports -> {
displayFragment(3) displayFragment(3)
} }
R.id.navigation_more -> { R.id.navigation_settings -> {
displayFragment(4) displayFragment(4)
} }
} }
@ -80,39 +75,50 @@ class HomeActivity : PokerAnalyticsActivity() {
} }
override fun onNewIntent(intent: Intent?) { // override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) // super.onNewIntent(intent)
//
intent?.let { // setIntent(intent)
// intent?.let {
when (intent.action) { //
"android.intent.action.VIEW" -> { // import // when (intent.action) {
val data = it.data // "android.intent.action.VIEW" -> { // import
if (data != null) { // val data = it.data
this.requestImportConfirmation(data) // if (data != null) {
} else { // this.requestImportConfirmation(data)
throw IllegalStateException("URI null on import") // } else {
} // throw PAIllegalStateException("URI null on import")
} // }
else -> { // }
Timber.d("Intent ${intent.action} unmanaged") // else -> {
} // Timber.w("Intent ${intent.action} unmanaged")
} // }
} // }
// }
//
// }
// override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
// super.onActivityResult(requestCode, resultCode, data)
//
// when (requestCode) {
// RequestCode.IMPORT.value -> {
// if (resultCode == ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value) {
// showAlertDialog(context = this, message = R.string.unknown_import_format_popup_message)
// }
// }
// }
// }
} // Import
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { // private fun requestImportConfirmation(uri: Uri) {
super.onActivityResult(requestCode, resultCode, data) //
// showAlertDialog(context = this, title = R.string.import_confirmation, showCancelButton = true, positiveAction = {
when (requestCode) { // ImportActivity.newInstanceForResult(this, uri)
RequestCode.IMPORT.value -> { // })
if (resultCode == ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value) { //
showAlertDialog(context = this, message = R.string.unknown_import_format_popup_message) // }
}
}
}
}
private fun observeRealmObjects() { private fun observeRealmObjects() {
@ -164,14 +170,4 @@ class HomeActivity : PokerAnalyticsActivity() {
viewPager.setCurrentItem(index, false) viewPager.setCurrentItem(index, false)
} }
// Import
private fun requestImportConfirmation(uri: Uri) {
showAlertDialog(context = this, title = R.string.import_confirmation, showCancelButton = true, positiveAction = {
ImportActivity.newInstanceForResult(this, uri)
})
}
} }

@ -4,12 +4,18 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import io.realm.Realm import io.realm.Realm
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.activity.components.ResultCode
import net.pokeranalytics.android.ui.extensions.showAlertDialog
import net.pokeranalytics.android.ui.fragment.ImportFragment import net.pokeranalytics.android.ui.fragment.ImportFragment
import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.extensions.count
import timber.log.Timber import timber.log.Timber
class ImportActivity : PokerAnalyticsActivity() { class ImportActivity : PokerAnalyticsActivity() {
@ -39,20 +45,12 @@ class ImportActivity : PokerAnalyticsActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
this.fileURI = intent.getParcelableExtra(ImportActivity.IntentKey.URI.keyName) intent?.data?.let {
this.fileURI = it
}
setContentView(R.layout.activity_import) setContentView(R.layout.activity_import)
initUI() requestImportConfirmation()
}
override fun onStop() {
super.onStop()
// Updates the main thread instance with newly inserted data
val realm = Realm.getDefaultInstance()
realm.refresh()
realm.close()
} }
private fun initUI() { private fun initUI() {
@ -71,16 +69,66 @@ class ImportActivity : PokerAnalyticsActivity() {
} }
// private fun requestPermission() { // override fun onNewIntent(intent: Intent?) {
// if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { // super.onNewIntent(intent)
// ActivityCompat.requestPermissions( //
// this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSION_REQUEST_ACCESS_FINE_LOCATION // Timber.d("++++++ data = ${intent?.data}")
// ) //
// setIntent(intent)
// intent?.let {
//
// when (intent.action) {
// "android.intent.action.VIEW" -> { // import
// val data = it.data
// if (data != null) {
// this.requestImportConfirmation(data)
// } else {
// throw PAIllegalStateException("URI null on import")
// }
// }
// else -> {
// Timber.w("Intent ${intent.action} unmanaged")
// }
// }
// } // }
// }
// //
// override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
// super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// } // }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RequestCode.IMPORT.value -> {
if (resultCode == ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value) {
showAlertDialog(context = this, message = R.string.unknown_import_format_popup_message, positiveAction = {
finish()
})
}
}
}
}
// Import
private fun requestImportConfirmation() {
val realm = Realm.getDefaultInstance()
val sessionCount = realm.count(Session::class.java)
realm.close()
if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
Toast.makeText(this, "Please subscribe!", Toast.LENGTH_LONG).show()
BillingActivity.newInstanceForResult(this, true)
return
}
showAlertDialog(context = this, title = R.string.import_confirmation, showCancelButton = true, positiveAction = {
initUI()
}, negativeAction = {
finish()
})
}
} }

@ -4,6 +4,8 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewAnimationUtils import android.view.ViewAnimationUtils
@ -12,6 +14,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
@ -29,11 +32,18 @@ class NewDataMenuActivity : PokerAnalyticsActivity() {
} }
} }
private var choiceSelected = false
private var menuWillBeHidden = false
private val fabSize = 48.px private val fabSize = 48.px
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(net.pokeranalytics.android.R.layout.activity_new_data)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // used to fix Oreo crash
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
setContentView(R.layout.activity_new_data)
initUI() initUI()
} }
@ -78,12 +88,18 @@ class NewDataMenuActivity : PokerAnalyticsActivity() {
* Set the result and hide menu * Set the result and hide menu
*/ */
private fun finishWithResult(choice: Int) { private fun finishWithResult(choice: Int) {
if (choiceSelected) {
return
}
choiceSelected = true
val intent = Intent() val intent = Intent()
intent.putExtra(IntentKey.CHOICE.keyName, choice) intent.putExtra(IntentKey.CHOICE.keyName, choice)
setResult(RESULT_OK, intent) setResult(RESULT_OK, intent)
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
delay(200) delay(200)
hideMenu(true) hideMenu()
} }
} }
@ -105,7 +121,12 @@ class NewDataMenuActivity : PokerAnalyticsActivity() {
/** /**
* Hide menu * Hide menu
*/ */
private fun hideMenu(hideQuickly: Boolean = false) { private fun hideMenu() {
if (menuWillBeHidden) {
return
}
menuWillBeHidden = true
val cx = menuContainer.measuredWidth - fabSize / 2 val cx = menuContainer.measuredWidth - fabSize / 2
val cy = menuContainer.measuredHeight - fabSize / 2 val cy = menuContainer.measuredHeight - fabSize / 2

@ -14,32 +14,32 @@ class SessionActivity: PokerAnalyticsActivity() {
enum class IntentKey(val keyName : String) { enum class IntentKey(val keyName : String) {
IS_TOURNAMENT("IS_TOURNAMENT"), IS_TOURNAMENT("IS_TOURNAMENT"),
DUPLICATE("DUPLICATE"),
SESSION_ID("SESSION_ID"); SESSION_ID("SESSION_ID");
} }
companion object { companion object {
fun newInstance(context: Context, isTournament: Boolean? = false, sessionId: String? = "") { fun newInstance(context: Context, isTournament: Boolean? = false, sessionId: String? = "", duplicate: Boolean = false) {
val intent = Intent(context, SessionActivity::class.java) val intent = this.intent(context, isTournament, sessionId, duplicate)
isTournament?.let {
intent.putExtra(IntentKey.IS_TOURNAMENT.keyName, isTournament)
}
sessionId?.let {
intent.putExtra(IntentKey.SESSION_ID.keyName, sessionId)
}
context.startActivity(intent) context.startActivity(intent)
} }
fun newInstanceforResult(fragment: Fragment, isTournament: Boolean? = false, sessionId: String? = "", requestCode: Int) { fun newInstanceforResult(fragment: Fragment, isTournament: Boolean? = false, sessionId: String? = "", duplicate: Boolean = false, requestCode: Int) {
val intent = Intent(fragment.requireContext(), SessionActivity::class.java) val intent = this.intent(fragment.requireContext(), isTournament, sessionId, duplicate)
fragment.startActivityForResult(intent, requestCode)
}
private fun intent(context: Context, isTournament: Boolean? = false, sessionId: String? = "", duplicate: Boolean = false) : Intent {
val intent = Intent(context, SessionActivity::class.java)
isTournament?.let { isTournament?.let {
intent.putExtra(IntentKey.IS_TOURNAMENT.keyName, isTournament) intent.putExtra(IntentKey.IS_TOURNAMENT.keyName, isTournament)
} }
intent.putExtra(IntentKey.DUPLICATE.keyName, duplicate)
sessionId?.let { sessionId?.let {
intent.putExtra(IntentKey.SESSION_ID.keyName, sessionId) intent.putExtra(IntentKey.SESSION_ID.keyName, sessionId)
} }
return intent
fragment.startActivityForResult(intent, requestCode)
} }
} }
@ -62,8 +62,9 @@ class SessionActivity: PokerAnalyticsActivity() {
private fun initUI() { private fun initUI() {
val sessionId = intent.getStringExtra(IntentKey.SESSION_ID.keyName) val sessionId = intent.getStringExtra(IntentKey.SESSION_ID.keyName)
val isTournament = intent.getBooleanExtra(IntentKey.IS_TOURNAMENT.keyName, false) val isTournament = intent.getBooleanExtra(IntentKey.IS_TOURNAMENT.keyName, false)
val duplicate = intent.getBooleanExtra(IntentKey.DUPLICATE.keyName, false)
val fragment = sessionFragment as SessionFragment val fragment = sessionFragment as SessionFragment
fragment.setData(isTournament, sessionId) fragment.setData(isTournament, sessionId, duplicate)
} }
} }

@ -0,0 +1,33 @@
package net.pokeranalytics.android.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
class Top10Activity : PokerAnalyticsActivity() {
companion object {
fun newInstance(context: Context) {
val intent = Intent(context, Top10Activity::class.java)
context.startActivity(intent)
}
/**
* Create a new instance for result
*/
fun newInstanceForResult(fragment: Fragment, requestCode: Int) {
val intent = Intent(fragment.requireContext(), Top10Activity::class.java)
fragment.startActivityForResult(intent, requestCode)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_top_10)
}
}

@ -2,10 +2,17 @@ package net.pokeranalytics.android.ui.activity.components
enum class RequestCode(var value: Int) { enum class RequestCode(var value: Int) {
DEFAULT(1), DEFAULT(1),
FEED_MENU(100),
FEED_TRANSACTION_DETAILS(101),
BANKROLL_DETAILS(700),
BANKROLL_CREATE(701),
BANKROLL_EDIT(702),
NEW_SESSION(800), NEW_SESSION(800),
NEW_TRANSACTION(801), NEW_TRANSACTION(801),
NEW_REPORT(802), NEW_REPORT(802),
IMPORT(900) IMPORT(900),
SUBSCRIPTION(901),
CURRENCY(902)
} }
enum class ResultCode(var value: Int) { enum class ResultCode(var value: Int) {

@ -1,6 +1,7 @@
package net.pokeranalytics.android.ui.activity.components package net.pokeranalytics.android.ui.activity.components
import android.Manifest.permission.ACCESS_FINE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle import android.os.PersistableBundle
@ -33,6 +34,7 @@ open class PokerAnalyticsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState) super.onCreate(savedInstanceState, persistentState)
Crashlytics.log("$this.localClassName onCreate") Crashlytics.log("$this.localClassName onCreate")
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT // fixes crash
} }
override fun onResume() { override fun onResume() {

@ -6,13 +6,17 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.row_feed_session.view.* import kotlinx.android.synthetic.main.row_feed_session.view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.BindableHolder import net.pokeranalytics.android.ui.view.BindableHolder
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.extensions.getMonthAndYear import net.pokeranalytics.android.util.extensions.getMonthAndYear
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -21,28 +25,54 @@ import kotlin.collections.HashMap
/** /**
* An adapter capable of displaying a list of RowRepresentables * An adapter capable of displaying a list of RowRepresentables
* @param dataSource the datasource providing rows * The [delegate] is an object notified of UI actions
* @param delegate the delegate, notified of UI actions
*/ */
class FeedSessionRowRepresentableAdapter( class FeedSessionRowRepresentableAdapter(
var delegate: RowRepresentableDelegate? = null, private var realm: Realm,
var realmResults: RealmResults<Session>, var delegate: RowRepresentableDelegate? = null
var pendingRealmResults: RealmResults<Session>,
var distinctHeaders: RealmResults<Session>
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var headersPositions = HashMap<Int, Date?>() private lateinit var startedSessions: RealmResults<Session>
private lateinit var pendingSessions: RealmResults<Session>
private lateinit var sortedHeaders: SortedMap<Int, Date?> private lateinit var sortedHeaders: SortedMap<Int, Date?>
private var allSessions = mutableListOf<Session>()
var filter: Filter? = null
set(value) {
field = value
defineSessions()
refreshData()
}
init { init {
defineSessions()
refreshData() refreshData()
} }
private fun defineSessions() {
this.startedSessions = requestNewQuery().isNotNull("startDate").findAll()
.sort("startDate", Sort.DESCENDING)
this.pendingSessions = requestNewQuery().isNull("startDate").findAll()
.sort("creationDate", Sort.DESCENDING)
}
private fun requestNewQuery() : RealmQuery<Session> {
this.filter?.let {
return it.query()
} ?: run {
return realm.where()
}
}
/** /**
* Display a session view * Display a session view
*/ */
inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
fun bind(position: Int, row: Session?, adapter: FeedSessionRowRepresentableAdapter) { fun bind(position: Int, row: Session?, adapter: FeedSessionRowRepresentableAdapter) {
itemView.sessionRow.setData(row as Session) itemView.sessionRow.setData(row as Session)
@ -50,6 +80,10 @@ class FeedSessionRowRepresentableAdapter(
adapter.delegate?.onRowSelected(position, row) adapter.delegate?.onRowSelected(position, row)
} }
itemView.sessionRow.setOnClickListener(listener) itemView.sessionRow.setOnClickListener(listener)
itemView.sessionRow.setOnLongClickListener {
itemView.showContextMenu()
}
} }
} }
@ -59,7 +93,7 @@ class FeedSessionRowRepresentableAdapter(
inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
fun bind(title: String) { fun bind(title: String) {
// Title // Title
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { itemView.findViewById<AppCompatTextView>(net.pokeranalytics.android.R.id.title)?.let {
it.text = title it.text = title
} }
} }
@ -67,25 +101,24 @@ class FeedSessionRowRepresentableAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == RowViewType.ROW_SESSION.ordinal) { return if (viewType == RowViewType.ROW_SESSION.ordinal) {
val layout = LayoutInflater.from(parent.context).inflate(R.layout.row_feed_session, parent, false) val layout = LayoutInflater.from(parent.context).inflate(net.pokeranalytics.android.R.layout.row_feed_session, parent, false)
RowSessionViewHolder(layout) RowSessionViewHolder(layout)
} else { } else {
val layout = LayoutInflater.from(parent.context).inflate(R.layout.row_header_title, parent, false) val layout = LayoutInflater.from(parent.context).inflate(net.pokeranalytics.android.R.layout.row_header_title, parent, false)
HeaderTitleViewHolder(layout) HeaderTitleViewHolder(layout)
} }
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
if (sortedHeaders.containsKey(position)) { return if (sortedHeaders.containsKey(position)) {
return RowViewType.HEADER_TITLE.ordinal RowViewType.HEADER_TITLE.ordinal
} else { } else {
return RowViewType.ROW_SESSION.ordinal RowViewType.ROW_SESSION.ordinal
} }
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return realmResults.size + pendingRealmResults.size + distinctHeaders.size return allSessions.size + sortedHeaders.size
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
@ -104,38 +137,34 @@ class FeedSessionRowRepresentableAdapter(
// If the header has no date, it's a pending session // If the header has no date, it's a pending session
return if (sortedHeaders[position] == null) { return if (sortedHeaders[position] == null) {
context.getString(R.string.pending) context.getString(net.pokeranalytics.android.R.string.pending)
} else { } else {
// Else, return the formatted date // Else, return the formatted date
val realmHeaderPosition = if (pendingRealmResults.size > 0) sortedHeaders.keys.indexOf(position) - 1 else sortedHeaders.keys.indexOf(position) sortedHeaders[position]?.getMonthAndYear() ?: throw PAIllegalStateException("Null date should not happen there")
distinctHeaders[realmHeaderPosition]?.startDate?.getMonthAndYear() ?: ""
} }
} }
return NULL_TEXT throw PAIllegalStateException("Any position should always have a header, position = $position")
}
fun sessionIdForPosition(position: Int): String? {
return this.getSessionForPosition(position)?.id
} }
/** /**
* Get real index * Get real index
*/ */
private fun getSessionForPosition(position: Int): Session? { private fun getSessionForPosition(position: Int): Session? {
return if (pendingRealmResults.size > 0 && position < pendingRealmResults.size + 1) {
// If we have pending session & the position is between these sessions
pendingRealmResults[position - 1]
} else {
// Else, return the correct session
// Row position
var headersBefore = 0
for (key in sortedHeaders.keys) {
if (position > key) {
headersBefore++
} else {
break
}
}
realmResults[position - headersBefore - pendingRealmResults.size] // Row position
var headersBefore = 0
for (key in sortedHeaders.keys) {
if (position > key) {
headersBefore++
} else {
break
}
} }
return allSessions[position - headersBefore]
} }
/** /**
@ -143,12 +172,12 @@ class FeedSessionRowRepresentableAdapter(
*/ */
fun refreshData() { fun refreshData() {
headersPositions.clear() allSessions.clear()
allSessions.addAll(this.pendingSessions)
allSessions.addAll(this.startedSessions)
Timber.d("Update session list, total count = ${allSessions.size}")
// If we have pending sessions, set the first header to null val headersPositions = HashMap<Int, Date?>()
if (pendingRealmResults.size > 0) {
headersPositions[0] = null
}
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
@ -158,28 +187,31 @@ class FeedSessionRowRepresentableAdapter(
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
// Add headers if the date doesn't exist yet // Add headers if the date doesn't exist yet
for ((index, session) in realmResults.withIndex()) { for ((index, session) in allSessions.withIndex()) {
calendar.time = session.startDate ?: session.creationDate
if (checkHeaderCondition(calendar, previousYear, previousMonth)) { val startDate = session.startDate
headersPositions[index + headersPositions.size + pendingRealmResults.size] = session.startDate ?: session.creationDate if (startDate == null) {
previousYear = calendar.get(Calendar.YEAR) headersPositions[0] = null
previousMonth = calendar.get(Calendar.MONTH) } else {
calendar.time = startDate
if (checkHeaderCondition(calendar, previousYear, previousMonth)) {
headersPositions[index + headersPositions.size] = startDate
previousYear = calendar.get(Calendar.YEAR)
previousMonth = calendar.get(Calendar.MONTH)
}
} }
} }
sortedHeaders = headersPositions.toSortedMap() sortedHeaders = headersPositions.toSortedMap()
Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms") Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms")
} }
/** /**
* Check if we need to add a header * Check if we need to add a header
* Can be change to manage different condition * Can be changed to manage different condition
*/ */
private fun checkHeaderCondition(currentCalendar: Calendar, previousYear: Int, previousMonth: Int) : Boolean { private fun checkHeaderCondition(currentCalendar: Calendar, previousYear: Int, previousMonth: Int) : Boolean {
return currentCalendar.get(Calendar.YEAR) == previousYear && currentCalendar.get(Calendar.MONTH) < previousMonth || (currentCalendar.get(Calendar.YEAR) < previousYear) return currentCalendar.get(Calendar.YEAR) == previousYear && currentCalendar.get(Calendar.MONTH) < previousMonth || (currentCalendar.get(Calendar.YEAR) < previousYear)
} }
} }

@ -40,6 +40,7 @@ class FeedTransactionRowRepresentableAdapter(
* Display a transaction view * Display a transaction view
*/ */
inner class RowTransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class RowTransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
fun bind(position: Int, row: Transaction?, adapter: FeedTransactionRowRepresentableAdapter) { fun bind(position: Int, row: Transaction?, adapter: FeedTransactionRowRepresentableAdapter) {
itemView.transactionRow.setData(row as Transaction) itemView.transactionRow.setData(row as Transaction)
@ -75,10 +76,10 @@ class FeedTransactionRowRepresentableAdapter(
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
if (sortedHeaders.containsKey(position)) { return if (sortedHeaders.containsKey(position)) {
return RowViewType.HEADER_TITLE.ordinal RowViewType.HEADER_TITLE.ordinal
} else { } else {
return RowViewType.ROW_TRANSACTION.ordinal RowViewType.ROW_TRANSACTION.ordinal
} }
} }
@ -130,8 +131,6 @@ class FeedTransactionRowRepresentableAdapter(
headersPositions.clear() headersPositions.clear()
val start = System.currentTimeMillis()
var previousYear = Int.MAX_VALUE var previousYear = Int.MAX_VALUE
var previousMonth = Int.MAX_VALUE var previousMonth = Int.MAX_VALUE
@ -148,6 +147,7 @@ class FeedTransactionRowRepresentableAdapter(
} }
sortedHeaders = headersPositions.toSortedMap() sortedHeaders = headersPositions.toSortedMap()
} }
/** /**

@ -21,7 +21,7 @@ class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAda
1 -> StatisticsFragment.newInstance() 1 -> StatisticsFragment.newInstance()
2 -> CalendarFragment.newInstance() 2 -> CalendarFragment.newInstance()
3 -> ReportsFragment.newInstance() 3 -> ReportsFragment.newInstance()
4 -> MoreFragment.newInstance() 4 -> SettingsFragment.newInstance()
else -> FeedFragment.newInstance() else -> FeedFragment.newInstance()
} }
} }
@ -47,7 +47,7 @@ class HomePagerAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAda
StatisticsFragment::class.java -> 1 StatisticsFragment::class.java -> 1
CalendarFragment::class.java -> 2 CalendarFragment::class.java -> 2
ReportsFragment::class.java -> 3 ReportsFragment::class.java -> 3
MoreFragment::class.java -> 4 SettingsFragment::class.java -> 4
else -> -1 else -> -1
} }
} }

@ -7,10 +7,13 @@ import net.pokeranalytics.android.ui.view.BindableHolder
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
interface RowRepresentableDelegate { interface RowRepresentableDelegate: RowEditableDelegate {
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 onRowDeleted(row: RowRepresentable) {}
interface RowEditableDelegate {
fun onRowValueChanged(value: Any?, row: RowRepresentable) {}
fun onRowDeleted(row: RowRepresentable) {}
} }
/** /**

@ -1,14 +1,14 @@
package net.pokeranalytics.android.ui.adapter package net.pokeranalytics.android.ui.adapter
import android.content.Context import android.content.Context
import net.pokeranalytics.android.exceptions.PAIllegalStateException
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.util.TextFormat import net.pokeranalytics.android.util.TextFormat
/** /**
* Base Interface to provide the RowRepresentable to the adapter * Base Interface to provide the RowRepresentable to the adapter
*/ */
interface RowRepresentableDataSource: EditableDataSource, DisplayableDataSource, SelectableDataSource { interface RowRepresentableDataSource: DisplayableDataSource, SelectableDataSource {
/** /**
* Returns a prebuild list of rows * Returns a prebuild list of rows
@ -42,21 +42,21 @@ interface StaticRowRepresentableDataSource: RowRepresentableDataSource {
this.adapterRows()?.let { this.adapterRows()?.let {
return it[position] return it[position]
} }
throw IllegalStateException("Need to implement Data Source") throw PAIllegalStateException("Need to implement Data Source")
} }
override fun numberOfRows(): Int { override fun numberOfRows(): Int {
this.adapterRows()?.let { this.adapterRows()?.let {
return it.size return it.size
} }
throw IllegalStateException("Need to implement Data Source") throw PAIllegalStateException("Need to implement Data Source")
} }
override fun viewTypeForPosition(position:Int): Int { override fun viewTypeForPosition(position:Int): Int {
this.rowRepresentableForPosition(position)?.let { this.rowRepresentableForPosition(position)?.let {
return it.viewType return it.viewType
} }
throw IllegalStateException("Need to implement Data Source") throw PAIllegalStateException("Need to implement Data Source")
} }
} }
@ -160,18 +160,6 @@ interface DisplayableDataSource {
} }
} }
/**
* An interface providing a way to describe how the edition of a [RowRepresentable] will be displayed
*/
interface EditableDataSource {
/**
* A list of [RowRepresentableEditDescriptor] object specifying the way the edition will be handled
*/
fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return null
}
}
/** /**
* An interface providing a way to select a row * An interface providing a way to select a row

@ -10,114 +10,83 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollReport import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.DataListActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
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
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class BankrollDetailsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object { companion object {
const val REQUEST_CODE_EDIT = 1000
/**
* Create new instance
*/
fun newInstance(bankrollReport: BankrollReport): BankrollDetailsFragment { fun newInstance(bankrollReport: BankrollReport): BankrollDetailsFragment {
val fragment = BankrollDetailsFragment() val fragment = BankrollDetailsFragment()
fragment.bankrollReport = bankrollReport fragment.bankrollId = bankrollReport.setup.bankrollId
return fragment return fragment
} }
} }
private var bankrollId: String? = null
private lateinit var bankroll: Bankroll
private var rows: ArrayList<RowRepresentable> = ArrayList()
private lateinit var bankrollAdapter: RowRepresentableAdapter private lateinit var bankrollAdapter: RowRepresentableAdapter
private lateinit var bankrollReport: BankrollReport
private var bankrollDetailsMenu: Menu? = null private var bankrollDetailsMenu: Menu? = null
private var rows: ArrayList<RowRepresentable> = ArrayList()
// Life Cycle // Life Cycle
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_bankroll_details, container, false) return inflater.inflate(R.layout.fragment_bankroll_details, container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initUI()
initData() initData()
initUI()
updateUI()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_EDIT && resultCode == RESULT_OK) { if (requestCode == RequestCode.BANKROLL_EDIT.value && resultCode == RESULT_OK) {
if (data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) { if (data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) {
activity?.setResult(RESULT_OK, data) activity?.setResult(RESULT_OK, data)
activity?.finish() activity?.finish()
} else { } else {
updateMenuUI() updateUI()
} }
} }
} }
override fun adapterRows(): List<RowRepresentable>? {
return rows
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
menu?.clear() menu?.clear()
inflater?.inflate(R.menu.toolbar_comparison_chart, menu) inflater?.inflate(R.menu.toolbar_comparison_chart, menu) // TODO R.menu.toolbar_comparison_chart?
this.bankrollDetailsMenu = menu this.bankrollDetailsMenu = menu
updateMenuUI() updateMenuUI()
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item!!.itemId) {
R.id.settings -> editBankroll()
}
return true
}
// Business
/** /**
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
rows.clear() this.bankrollId?.let { id ->
this.bankroll = getRealm().findById(id) ?: throw PAIllegalStateException("Bankroll not found, id=$id")
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.global))
val totalComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total)
val netComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netResult)
val netBankedComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netBanked)
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.bankroll, computedStat = totalComputedStat))
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.net_result, computedStat = netComputedStat))
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, resId = R.string.net_banked, computedStat = netBankedComputedStat))
if (bankrollReport.transactionBuckets.isNotEmpty()) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.operations))
bankrollReport.transactionBuckets.keys.forEach { key ->
bankrollReport.transactionBuckets[key]?.let { transactionBucket ->
val typeName = transactionBucket.transactions.firstOrNull()?.type?.getDisplayName(requireContext())
val computedStat = ComputedStat(Stat.NET_RESULT, transactionBucket.total)
rows.add(CustomizableRowRepresentable(RowViewType.TITLE_VALUE, title = typeName, computedStat = computedStat))
}
}
} }
} }
@ -128,8 +97,7 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
private fun initUI() { private fun initUI() {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
this.updateMenuUI()
updateMenuUI()
bankrollAdapter = RowRepresentableAdapter(this, this) bankrollAdapter = RowRepresentableAdapter(this, this)
@ -142,26 +110,107 @@ class BankrollDetailsFragment : PokerAnalyticsFragment(), StaticRowRepresentable
} }
} }
private fun updateUI() {
this.updateMenuUI()
Timber.d("BankrollDetailsFragment > updateUI > reportForBankroll")
BankrollReportManager.reportForBankroll(this.bankrollId) { bankrollReport ->
this.initRows(bankrollReport)
this.bankrollAdapter.notifyDataSetChanged()
}
}
private fun initRows(bankrollReport: BankrollReport) {
rows.clear()
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.global))
val currency = if (this.bankrollId != null) { this.bankroll.utilCurrency } else { null }
val totalComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total, currency = currency)
val netComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netResult, currency = currency)
val netBankedComputedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.netBanked, currency = currency)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.bankroll,
computedStat = totalComputedStat
)
)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.net_result,
computedStat = netComputedStat
)
)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
resId = R.string.net_banked,
computedStat = netBankedComputedStat
)
)
if (bankrollReport.transactionBuckets.isNotEmpty()) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.operations))
bankrollReport.transactionBuckets.keys.forEach { key ->
bankrollReport.transactionBuckets[key]?.let { transactionBucket ->
val typeName = transactionBucket.name
val computedStat = ComputedStat(Stat.NET_RESULT, transactionBucket.total, currency = currency)
rows.add(
CustomizableRowRepresentable(
RowViewType.TITLE_VALUE,
title = typeName,
computedStat = computedStat
)
)
}
}
}
}
/** /**
* Update menu UI * Update menu UI
*/ */
private fun updateMenuUI() { private fun updateMenuUI() {
if (bankrollReport.setup.virtualBankroll) { if (this.bankrollId == null) {
setToolbarTitle(getString(R.string.total)) setToolbarTitle(getString(R.string.total))
bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = false bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = false
} else { } else {
setToolbarTitle(bankrollReport.setup.bankroll?.name) setToolbarTitle(this.bankroll.name)
bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = true bankrollDetailsMenu?.findItem(R.id.settings)?.isVisible = true
} }
}
// StaticRowRepresentableDataSource
override fun adapterRows(): List<RowRepresentable>? {
return rows
}
// RowRepresentableDelegate
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.settings -> editBankroll()
}
return true
}
// Business
/** /**
* Open Bankroll edit activity * Open Bankroll edit activity
*/ */
private fun editBankroll() { private fun editBankroll() {
EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL, bankrollReport.setup.bankroll?.id, REQUEST_CODE_EDIT) EditableDataActivity.newInstanceForResult(this, LiveData.BANKROLL, this.bankrollId, RequestCode.BANKROLL_EDIT.value)
} }
} }

@ -8,7 +8,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.data.LineDataSet
import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_bankroll.* import kotlinx.android.synthetic.main.fragment_bankroll.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -16,39 +15,38 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.ComputedStat import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.bankroll.BankrollCalculator
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportSetup
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity
import net.pokeranalytics.android.ui.activity.DataListActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.GraphActivity import net.pokeranalytics.android.ui.activity.GraphActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
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
import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollGraphRow
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollMainRow
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollTotalRow
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.GraphRow
import net.pokeranalytics.android.util.extensions.sorted import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
interface BankrollRowRepresentable : RowRepresentable {
var bankrollId: String?
}
class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object { companion object {
const val REQUEST_CODE_DETAILS = 100
const val REQUEST_CODE_CREATE = 101
/** /**
* Create new instance * Create new instance
*/ */
@ -61,14 +59,11 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
} }
private var rows: ArrayList<RowRepresentable> = ArrayList() private var rows: ArrayList<RowRepresentable> = ArrayList()
private var bankrollReportForRow: HashMap<RowRepresentable, BankrollReport> = HashMap() private var bankrollRowRepresentables: HashMap<String?, List<BankrollRowRepresentable>> = HashMap()
private var lastItemClickedPosition: Int = 0
private var lastItemClickedId: String = ""
private var deletedRow: RowRepresentable? = null
private lateinit var bankrolls: RealmResults<Bankroll> private lateinit var bankrolls: RealmResults<Bankroll>
override fun deletableItems() : List<Deletable> { override fun deletableItems(): List<Deletable> {
return this.bankrolls return this.bankrolls
} }
@ -85,101 +80,68 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
initData() initData()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) { if (requestCode == RequestCode.BANKROLL_DETAILS.value && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id -> itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
delay(300) delay(300)
deleteItem(dataListAdapter, bankrolls, id) deleteItem(dataListAdapter, bankrolls, id)
// update view
BankrollReportManager.notifyBankrollReportImpact(id)
dataListAdapter.notifyDataSetChanged()
} }
} }
} else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) { } else if (requestCode == RequestCode.BANKROLL_CREATE.value && resultCode == Activity.RESULT_OK) {
//TODO: Refresh bankrolls //TODO: Refresh bankrolls
initData() initData()
} else {
initData()
} }
} }
override fun adapterRows(): List<RowRepresentable>? {
return rows
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) {
is GraphRow -> {
val lineDataSet = row.dataSet as LineDataSet
GraphActivity.newInstance(requireContext(), listOf(lineDataSet), title = getString(R.string.bankroll))
}
else -> {
if (bankrollReportForRow.containsKey(row)) {
bankrollReportForRow[row]?.let { bankrollReport ->
lastItemClickedPosition = position
lastItemClickedId = (row as? Identifiable)?.id ?: ""
BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, REQUEST_CODE_DETAILS)
}
}
}
}
}
// Business
/** /**
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
val realm = getRealm() this.bankrolls = getRealm().sorted()
this.bankrolls = realm.sorted() this.bankrolls.addChangeListener { _, _ ->
this.createRowRepresentables()
rows.clear() }
bankrollReportForRow.clear()
GlobalScope.launch {
launch(Dispatchers.Main) {
// TODO: Improve that
// We are in the main thread...
val startDate = Date() this.createRowRepresentables()
}
// Graph private fun createRowRepresentables() {
val globalBankrollReportSetup = BankrollReportSetup()
val globalBankrollReport = BankrollCalculator.computeReport(getRealm(), globalBankrollReportSetup)
rows.add(0, GraphRow(dataSet = globalBankrollReport.lineDataSet(requireContext())))
rows.add(globalBankrollReport)
bankrollReportForRow[globalBankrollReport] = globalBankrollReport
// Bankrolls rows.clear()
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.bankrolls)) bankrollRowRepresentables.clear()
Timber.d("initData: ${System.currentTimeMillis() - startDate.time}ms") // Virtual bankroll
val graphRow = BankrollGraphRow()
rows.add(0, graphRow)
val mainRow = BankrollMainRow()
rows.add(mainRow)
// val bankrolls = LiveData.Bankroll.items(getRealm()) as RealmResults<Bankroll> bankrollRowRepresentables[null] = listOf(graphRow, mainRow)
bankrolls.forEach { bankroll -> // Bankrolls
val bankrollReportSetup = BankrollReportSetup(bankroll) rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, resId = R.string.bankrolls))
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
val computedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total)
val row =
CustomizableRowRepresentable(RowViewType.TITLE_VALUE_ARROW, title = bankroll.name, computedStat = computedStat, isSelectable = true)
row.id = bankroll.id
rows.add(row) bankrolls.forEach { bankroll ->
bankrollReportForRow[row] = bankrollReport Timber.d("Creating row for br : ${bankroll.id}, name= ${bankroll.name}, isManaged = ${bankroll.isManaged()}, isValid = ${bankroll.isValid}")
} val row = BankrollTotalRow(bankroll.id, bankroll.name)
rows.add(row)
if (!isDetached) { bankrollRowRepresentables[bankroll.id] = listOf(row)
dataListAdapter.notifyDataSetChanged()
}
}
} }
dataListAdapter.notifyDataSetChanged()
} }
/** /**
@ -200,29 +162,31 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
} }
addButton.setOnClickListener { addButton.setOnClickListener {
EditableDataActivity.newInstanceForResult(this@BankrollFragment, dataType = LiveData.BANKROLL, primaryKey = null, requestCode = REQUEST_CODE_CREATE) EditableDataActivity.newInstanceForResult(
this@BankrollFragment,
dataType = LiveData.BANKROLL,
primaryKey = null,
requestCode = RequestCode.BANKROLL_CREATE.value
)
} }
} }
override fun updateUIAfterDeletion(itemPosition: Int) { override fun adapterRows(): List<RowRepresentable>? {
lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false } return rows
deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false }
rows.removeAt(lastItemClickedPosition)
dataListAdapter.notifyItemRemoved(lastItemClickedPosition)
} }
override fun updateUIAfterUndoDeletion(newItem: RealmObject) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) {
// TODO: Improve that is BankrollGraphRow -> {
// We are recreating a Bankroll report because the last one is invalid => the bankroll of the setup has been deleted val lineDataSet = row.dataSet as LineDataSet
GraphActivity.newInstance(requireContext(), listOf(lineDataSet), title = getString(R.string.bankroll))
deletedRow?.let { row -> }
val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll) is BankrollRowRepresentable -> {
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup) Timber.d("BankrollFragment > onRowSelected > reportForBankroll")
bankrollReportForRow[row] = bankrollReport BankrollReportManager.reportForBankroll(row.bankrollId) { bankrollReport ->
BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, RequestCode.BANKROLL_DETAILS.value)
rows.add(lastItemClickedPosition, row) }
dataListAdapter.notifyItemInserted(lastItemClickedPosition) }
} }
} }

@ -8,6 +8,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel import io.realm.RealmModel
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_calendar.* import kotlinx.android.synthetic.main.fragment_calendar.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -84,10 +85,13 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initData() initData()
initUI() initUI()
launchStatComputation()
} }
override fun onResume() {
super.onResume()
launchStatComputation()
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return rows return rows
} }
@ -117,7 +121,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
override val observedEntities: List<Class<out RealmModel>> = listOf(ComputableResult::class.java) override val observedEntities: List<Class<out RealmModel>> = listOf(ComputableResult::class.java)
override fun entitiesChanged(clazz: Class<out RealmModel>) { override fun entitiesChanged(clazz: Class<out RealmModel>, results: RealmResults<out RealmModel>) {
launchStatComputation() launchStatComputation()
} }

@ -17,6 +17,7 @@ import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRow import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRow
import net.pokeranalytics.android.util.UserDefaults
import java.util.* import java.util.*
class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
@ -47,12 +48,8 @@ class CurrenciesFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataS
private val availableCurrencies = this.systemCurrencies.filter { private val availableCurrencies = this.systemCurrencies.filter {
!mostUsedCurrencyCodes.contains(it.currencyCode) !mostUsedCurrencyCodes.contains(it.currencyCode)
}.filter { }.filter {
Locale.getAvailableLocales().filter { locale -> UserDefaults.availableCurrencyLocales.filter { currencyLocale ->
try { Currency.getInstance(currencyLocale).currencyCode == it.currencyCode
Currency.getInstance(locale).currencyCode == it.currencyCode
} catch (e: Exception) {
false
}
}.isNotEmpty() }.isNotEmpty()
}.sortedBy { }.sortedBy {
it.displayName it.displayName

@ -13,6 +13,7 @@ import io.realm.Realm
import io.realm.RealmResults import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.* import kotlinx.android.synthetic.main.fragment_data_list.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.interfaces.Identifiable
@ -24,7 +25,6 @@ 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.fragment.components.DeletableItemFragment import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment
import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback
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.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.sorted import net.pokeranalytics.android.util.extensions.sorted
@ -44,7 +44,7 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS
/** /**
* Set fragment data * Set fragment data
*/ */
fun setData(dataType: Int) { open fun setData(dataType: Int) {
this.dataType = LiveData.values()[dataType] this.dataType = LiveData.values()[dataType]
this.identifiableClass = this.dataType.relatedEntity this.identifiableClass = this.dataType.relatedEntity
@ -87,7 +87,7 @@ open class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataS
val itemId = item.id val itemId = item.id
deleteItem(dataListAdapter, items, itemId) deleteItem(dataListAdapter, items, itemId)
} else { } else {
throw IllegalStateException("Item with position $position not found") throw PAIllegalStateException("Item with position $position not found")
} }
} }

@ -3,9 +3,7 @@ package net.pokeranalytics.android.ui.fragment
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.*
import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -17,6 +15,7 @@ import io.realm.Sort
import io.realm.kotlin.where import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_feed.* import kotlinx.android.synthetic.main.fragment_feed.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Editable import net.pokeranalytics.android.model.interfaces.Editable
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
@ -30,13 +29,15 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.FilterableFragment import net.pokeranalytics.android.ui.fragment.components.FilterableFragment
import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode import net.pokeranalytics.android.ui.interfaces.FilterActivityRequestCode
import net.pokeranalytics.android.ui.interfaces.FilterableType import net.pokeranalytics.android.ui.interfaces.FilterableType
import net.pokeranalytics.android.ui.view.ContextMenuRecyclerView
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.extensions.count
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
class FeedFragment : FilterableFragment(), RowRepresentableDelegate { class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
private enum class Tab { private enum class Tab {
@ -46,20 +47,20 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
companion object { companion object {
const val REQUEST_CODE_MENU = 100
const val REQUEST_CODE_TRANSACTION_DETAILS = 101
fun newInstance(): FeedFragment { fun newInstance(): FeedFragment {
val fragment = FeedFragment() val fragment = FeedFragment()
val bundle = Bundle() val bundle = Bundle()
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment
} }
} }
private var currentTab = Tab.SESSIONS
private lateinit var feedSessionAdapter: FeedSessionRowRepresentableAdapter private lateinit var feedSessionAdapter: FeedSessionRowRepresentableAdapter
private lateinit var feedTransactionAdapter: FeedTransactionRowRepresentableAdapter private lateinit var feedTransactionAdapter: FeedTransactionRowRepresentableAdapter
private lateinit var realmSessions: RealmResults<Session>
private lateinit var realmTransactions: RealmResults<Transaction> private lateinit var realmTransactions: RealmResults<Transaction>
private lateinit var betaLimitDate: Date private lateinit var betaLimitDate: Date
@ -70,8 +71,8 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
override val observedEntities: List<Class<out RealmModel>> = listOf(Session::class.java, Transaction::class.java) override val observedEntities: List<Class<out RealmModel>> = listOf(Session::class.java, Transaction::class.java)
override fun entitiesChanged(clazz: Class<out RealmModel>) { override fun entitiesChanged(clazz: Class<out RealmModel>, results: RealmResults<out RealmModel>) {
super.entitiesChanged(clazz) super.entitiesChanged(clazz, results)
when (clazz.kotlin) { when (clazz.kotlin) {
Session::class -> { Session::class -> {
@ -91,6 +92,34 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
return inflater.inflate(R.layout.fragment_feed, container, false) return inflater.inflate(R.layout.fragment_feed, container, false)
} }
override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
super.onCreateContextMenu(menu, v, menuInfo)
if (v?.id == R.id.menuRecyclerView) {
activity?.menuInflater?.inflate(R.menu.menu_session, menu)
}
}
override fun onContextItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.duplicate -> {
val info = item.menuInfo as ContextMenuRecyclerView.RecyclerViewContextMenuInfo
val sessionId = this.feedSessionAdapter.sessionIdForPosition(info.position)
if (sessionId != null) {
createNewSession(true, sessionId = sessionId, duplicate = true)
} else {
throw PAIllegalStateException("Session not found for duplicate at position: ${info.position}")
}
}
else -> {
}
}
return true
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initUI() initUI()
@ -99,13 +128,13 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_MENU && resultCode == RESULT_OK && data != null) { if (requestCode == RequestCode.FEED_MENU.value && resultCode == RESULT_OK && data != null) {
when (data.getIntExtra(NewDataMenuActivity.IntentKey.CHOICE.keyName, -1)) { when (data.getIntExtra(NewDataMenuActivity.IntentKey.CHOICE.keyName, -1)) {
0 -> createNewSession(false) 0 -> createNewSession(false)
1 -> createNewSession(true) 1 -> createNewSession(true)
2 -> createNewTransaction() 2 -> createNewTransaction()
} }
} else if (requestCode == REQUEST_CODE_TRANSACTION_DETAILS && resultCode == RESULT_OK && data != null) { } else if (requestCode == RequestCode.FEED_TRANSACTION_DETAILS.value && resultCode == RESULT_OK && data != null) {
if (data.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) { if (data.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName) != null) {
deleteSelectedTransaction() deleteSelectedTransaction()
} }
@ -123,22 +152,9 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
realmSessions.removeAllChangeListeners()
realmTransactions.removeAllChangeListeners() realmTransactions.removeAllChangeListeners()
} }
/*
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser && view != null) {
if (FilterHandler.filterWasUpdated) {
applyFilter()
FilterHandler.filterWasUpdated = false
}
}
}
*/
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) { when (row) {
is Session -> SessionActivity.newInstance(requireContext(), sessionId = (row as Editable).id) is Session -> SessionActivity.newInstance(requireContext(), sessionId = (row as Editable).id)
@ -149,7 +165,7 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
this, this,
LiveData.TRANSACTION, LiveData.TRANSACTION,
row.id, row.id,
REQUEST_CODE_TRANSACTION_DETAILS RequestCode.FEED_TRANSACTION_DETAILS.value
) )
} }
} }
@ -160,37 +176,49 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
*/ */
private fun initUI() { private fun initUI() {
disclaimerContainer.isVisible = Preferences.shouldShowDisclaimer(requireContext()) this.feedSessionAdapter = FeedSessionRowRepresentableAdapter(getRealm(), this)
registerForContextMenu(this.menuRecyclerView)
val messageToShow: Preferences.FeedMessage? = Preferences.feedMessageToShow(requireContext())
if (messageToShow != null) {
messageBox.isVisible = true
message.text = getString(messageToShow.resId)
disclaimerDismiss.setOnClickListener { messageBoxDismiss.setOnClickListener {
Preferences.setStopShowingDisclaimer(requireContext()) Preferences.setStopShowingMessage(messageToShow, requireContext())
disclaimerContainer.animate().translationY(disclaimerContainer.height.toFloat()) messageBox.animate().translationY(messageBox.height.toFloat())
.setInterpolator(FastOutSlowInInterpolator()) .setInterpolator(FastOutSlowInInterpolator())
.withEndAction { disclaimerContainer?.isVisible = false } .withEndAction { messageBox?.isVisible = false }
.start() .start()
}
} else {
messageBox.isVisible = false
} }
// Add button
addButton.setOnClickListener { addButton.setOnClickListener {
activity?.let { activity?.let {
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(it) val options = ActivityOptionsCompat.makeSceneTransitionAnimation(it)
val intent = Intent(requireContext(), NewDataMenuActivity::class.java) val intent = Intent(requireContext(), NewDataMenuActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_MENU, options.toBundle()) startActivityForResult(intent, RequestCode.FEED_MENU.value, options.toBundle())
} }
} }
// Tabs
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 -> { Tab.SESSIONS.ordinal -> {
currentFilterable = FilterableType.SESSION currentFilterable = FilterableType.SESSION
recyclerView.adapter = feedSessionAdapter
} }
1 -> { Tab.TRANSACTIONS.ordinal -> {
currentFilterable = FilterableType.TRANSACTION currentFilterable = FilterableType.TRANSACTION
recyclerView.adapter = feedTransactionAdapter
} }
} }
tabChanged(tab.position)
} }
override fun onTabUnselected(tab: TabLayout.Tab) { override fun onTabUnselected(tab: TabLayout.Tab) {
@ -209,10 +237,10 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
val sdf = SimpleDateFormat("dd/M/yyyy hh:mm", Locale.getDefault()) val sdf = SimpleDateFormat("dd/M/yyyy hh:mm", Locale.getDefault())
betaLimitDate = sdf.parse("17/7/2019 10:00") betaLimitDate = sdf.parse("17/7/2019 10:00")
this.currentFilterable = FilterableType.SESSION this.currentFilterable = FilterableType.SESSION
val viewManager = SmoothScrollLinearLayoutManager(requireContext()) val viewManager = SmoothScrollLinearLayoutManager(requireContext())
recyclerView.apply { menuRecyclerView.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = viewManager layoutManager = viewManager
} }
@ -221,30 +249,15 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
} }
private fun loadSessions(filter: Filter? = null) { private fun loadSessions(filter: Filter? = null) {
val sessionFilter: Filter? = filter?.let {
if (it.filterableType == FilterableType.SESSION) {
it
} else {
null
}
}
// Sessions when (filter?.filterableType) {
this.realmSessions = FilterableType.SESSION -> {
sessionFilter?.results() ?: run { getRealm().where<Session>().isNotNull("startDate").findAll() } this.feedSessionAdapter.filter = filter
this.realmSessions = this.realmSessions.sort("startDate", Sort.DESCENDING) }
else -> {
val pendingSessions = sessionFilter?.let { this.feedSessionAdapter.filter = null
getRealm().where<Session>().alwaysFalse().findAll() }
} ?: run {
getRealm().where<Session>().isNull("year").isNull("month").findAll().sort("startDate", Sort.DESCENDING)
}
var distinctDateSessions = sessionFilter?.results("year", "month") ?: run {
getRealm().where<Session>().distinct("year", "month").findAll()
} }
distinctDateSessions = distinctDateSessions.sort("startDate", Sort.DESCENDING)
this.feedSessionAdapter =
FeedSessionRowRepresentableAdapter(this, realmSessions, pendingSessions, distinctDateSessions)
} }
private fun loadTransactions(filter: Filter? = null) { private fun loadTransactions(filter: Filter? = null) {
@ -272,21 +285,27 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
/** /**
* Create a new cash game * Create a new cash game
*/ */
private fun createNewSession(isTournament: Boolean) { private fun createNewSession(isTournament: Boolean, sessionId: String? = null, duplicate: Boolean = false) {
// val sessionCount = this.feedSessionAdapter.realmResults.size
// if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
// Toast.makeText(context, "Please subscribe!", Toast.LENGTH_LONG).show()
// BillingActivity.newInstanceForResult(requireContext())
// return
// }
if (Date().after(betaLimitDate)) { val sessionCount = getRealm().count(Session::class.java)
this.showEndOfBetaMessage() if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
BillingActivity.newInstanceForResult(this, true)
return return
} }
SessionActivity.newInstanceforResult(this, isTournament, requestCode = RequestCode.NEW_SESSION.value) // Keep commented code for special versions
// if (Date().after(betaLimitDate)) {
// this.showEndOfBetaMessage()
// return
// }
SessionActivity.newInstanceforResult(
this,
isTournament,
sessionId = sessionId,
duplicate = duplicate,
requestCode = RequestCode.NEW_SESSION.value
)
newSessionCreated = true newSessionCreated = true
} }
@ -295,38 +314,36 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
*/ */
private fun createNewTransaction() { private fun createNewTransaction() {
if (Date().after(betaLimitDate)) { // if (Date().after(betaLimitDate)) {
this.showEndOfBetaMessage() // this.showEndOfBetaMessage()
return // return
} // }
EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION, null, RequestCode.NEW_TRANSACTION.value) EditableDataActivity.newInstanceForResult(this, LiveData.TRANSACTION, null, RequestCode.NEW_TRANSACTION.value)
// EditableDataActivity.newInstance(requireContext(), LiveData.TRANSACTION.ordinal)
} }
/** /**
* Delete selected transaction * Delete selected transaction
*/ */
private fun deleteSelectedTransaction() { private fun deleteSelectedTransaction() {
val realm = getRealm() getRealm().executeTransaction {
realm.beginTransaction() selectedTransaction?.deleteFromRealm()
selectedTransaction?.deleteFromRealm() }
realm.commitTransaction()
selectedTransactionPosition = -1 selectedTransactionPosition = -1
} }
/** /**
* Show end of beta message * Show end of beta message
* Keep for possible future uses
*/ */
private fun showEndOfBetaMessage() { private fun showEndOfBetaMessage() {
Toast.makeText( Toast.makeText(
context, context,
"Beta has ended. Thanks a lot for your participation! Please update with the Google Play version to continue using the app", "App version has ended. Thanks a lot for using it! Please update with the Google Play version to continue using the app.",
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
// Filter Handler // Filter Handler
override fun applyFilter() { override fun applyFilter() {
@ -337,41 +354,48 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate {
this.loadTransactions(filter) this.loadTransactions(filter)
filter?.let { filter?.let {
when (it.filterableType) { when (it.filterableType) {
FilterableType.SESSION -> { FilterableType.SESSION -> {
recyclerView.adapter = feedSessionAdapter this.selectTab(Tab.SESSIONS)
this.selectTab(Tab.SESSIONS) }
} FilterableType.TRANSACTION -> {
FilterableType.TRANSACTION -> { this.selectTab(Tab.TRANSACTIONS)
recyclerView.adapter = feedTransactionAdapter }
this.selectTab(Tab.TRANSACTIONS) else -> {
} }
else -> { }
}
}
adapterHasBeenSet = true adapterHasBeenSet = true
} }
if (!adapterHasBeenSet) { if (!adapterHasBeenSet) {
adapterHasBeenSet = true adapterHasBeenSet = true
recyclerView.adapter = feedSessionAdapter this.setAdapter()
} }
} }
override fun removeFilter() { override fun removeFilter() {
super.removeFilter() super.removeFilter()
this.loadSessions() this.loadSessions()
this.loadTransactions() this.loadTransactions()
if (currentFilterable == FilterableType.SESSION) { this.setAdapter()
recyclerView.adapter = feedSessionAdapter
} else {
recyclerView.adapter = feedTransactionAdapter
}
} }
private fun selectTab(tab: Tab) { private fun selectTab(tab: Tab) {
this.currentTab = tab
this.tabs.getTabAt(tab.ordinal)?.select() this.tabs.getTabAt(tab.ordinal)?.select()
setAdapter()
}
private fun tabChanged(index: Int) {
this.currentTab = Tab.values()[index]
setAdapter()
}
private fun setAdapter() {
when (this.currentTab) {
Tab.SESSIONS -> menuRecyclerView.adapter = feedSessionAdapter
Tab.TRANSACTIONS -> menuRecyclerView.adapter = feedTransactionAdapter
}
} }
} }

@ -18,8 +18,6 @@ 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
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.helpers.DateTimePickerManager
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow import net.pokeranalytics.android.ui.view.rowrepresentable.FilterCategoryRow
@ -65,35 +63,7 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo
updateRowsSelection(row) updateRowsSelection(row)
return return
} }
row.startEditing(null, this)
when (row) {
is QueryCondition.DateQuery -> DateTimePickerManager.create(
requireContext(),
row,
this,
row.singleValue,
onlyDate = !row.showTime,
onlyTime = row.showTime
)
is QueryCondition.Duration -> {
var hours: String? = null
var minutes: String? = null
row.minutes?.let {
hours = if (it / 60 > 0) (it / 60).toString() else null
minutes = if (it % 60 > 0) (it % 60).toString() else null
}
val data = row.editingDescriptors(mapOf("hours" to hours, "minutes" to minutes))
BottomSheetFragment.create(fragmentManager, row, this, data, true)
}
is QueryCondition.ListOfValues<*> -> {
var valueAsString: String? = null
row.listOfValues.firstOrNull()?.let {
valueAsString = row.listOfValues.firstOrNull()?.toString()
}
val data = row.editingDescriptors(mapOf("valueAsString" to valueAsString))
BottomSheetFragment.create(fragmentManager, row, this, data, true)
}
}
} }
override fun stringForRow(row: RowRepresentable, context: Context): String { override fun stringForRow(row: RowRepresentable, context: Context): String {
@ -243,12 +213,10 @@ open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSo
Timber.d("Selected rows: $it") Timber.d("Selected rows: $it")
} }
val realm = getRealm() getRealm().executeTransaction {
realm.beginTransaction() currentFilter?.remove(filterCategoryRow)
currentFilter?.remove(filterCategoryRow) currentFilter?.createOrUpdateFilterConditions(selectedRows)
currentFilter?.createOrUpdateFilterConditions(selectedRows) }
realm.commitTransaction()
currentFilter?.filterConditions?.forEach { currentFilter?.filterConditions?.forEach {
Timber.d("Condition: $it") Timber.d("Condition: $it")
} }

@ -12,11 +12,12 @@ import kotlinx.android.synthetic.main.fragment_editable_data.recyclerView
import kotlinx.android.synthetic.main.fragment_filters.* import kotlinx.android.synthetic.main.fragment_filters.*
import kotlinx.android.synthetic.main.fragment_filters.view.toolbar import kotlinx.android.synthetic.main.fragment_filters.view.toolbar
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.FilterDetailsActivity import net.pokeranalytics.android.ui.activity.FilterDetailsActivity
import net.pokeranalytics.android.ui.activity.FiltersActivity import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.activity.FiltersListActivity
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
@ -27,6 +28,7 @@ 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 net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.sorted import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber import timber.log.Timber
@ -54,6 +56,14 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
private var isUpdating = false private var isUpdating = false
private var showMostUsedFiltersLayout = true private var showMostUsedFiltersLayout = true
/**
* Set fragment data
*/
fun setData(primaryKey: String?, filterableType: FilterableType) {
this.primaryKey = primaryKey
this.filterableType = filterableType
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState) super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_filters, container, false) return inflater.inflate(R.layout.fragment_filters, container, false)
@ -159,7 +169,7 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
moreFilters.setOnClickListener { moreFilters.setOnClickListener {
LiveData.FILTER.subType = filterableType.uniqueIdentifier LiveData.FILTER.subType = filterableType.uniqueIdentifier
DataListActivity.newSelectInstance(this, LiveData.FILTER.ordinal, false) FiltersListActivity.newSelectInstance(this, LiveData.FILTER.ordinal, false)
} }
mostUsedFiltersLayout.isVisible = showMostUsedFiltersLayout mostUsedFiltersLayout.isVisible = showMostUsedFiltersLayout
@ -173,7 +183,9 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
val realm = getRealm() val realm = getRealm()
primaryKey?.let { primaryKey?.let {
currentFilter = realm.copyFromRealm(Filter.getFilterBydId(realm, it))
val filter = realm.findById<Filter>(it) ?: throw PAIllegalStateException("Can't find filter with id=$it")
currentFilter = realm.copyFromRealm(filter)
isUpdating = true isUpdating = true
} ?: run { } ?: run {
currentFilter = Filter.newInstance(this.filterableType.uniqueIdentifier) //realm.copyFromRealm(Filter.newInstanceForResult(realm, this.filterableType.ordinal)) currentFilter = Filter.newInstance(this.filterableType.uniqueIdentifier) //realm.copyFromRealm(Filter.newInstanceForResult(realm, this.filterableType.ordinal))
@ -251,13 +263,12 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
* Validate the updates of the queryWith * Validate the updates of the queryWith
*/ */
private fun validateUpdates() { private fun validateUpdates() {
val realm = getRealm() getRealm().executeTransaction { realm ->
realm.beginTransaction() currentFilter?.let {
currentFilter?.let { it.name = it.query.getName(requireContext())
it.name = it.query.getName(requireContext()) realm.copyToRealmOrUpdate(it)
realm.copyToRealmOrUpdate(it) }
} }
realm.commitTransaction()
val filterId = currentFilter?.id ?: "" val filterId = currentFilter?.id ?: ""
finishActivityWithResult(filterId) finishActivityWithResult(filterId)
@ -268,13 +279,12 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
*/ */
private fun cancelUpdates() { private fun cancelUpdates() {
val filterId = filterCopy?.id ?: "" val filterId = filterCopy?.id ?: ""
val realm = getRealm() getRealm().executeTransaction { realm ->
realm.beginTransaction() filterCopy?.let {
filterCopy?.let { realm.copyToRealmOrUpdate(it)
realm.copyToRealmOrUpdate(it) }
} }
realm.commitTransaction() finishActivityWithResult(filterId)
finishActivityWithResult(filterId)
} }
/** /**
@ -287,14 +297,6 @@ open class FiltersFragment : RealmFragment(), StaticRowRepresentableDataSource,
activity?.finish() activity?.finish()
} }
/**
* Set fragment data
*/
fun setData(primaryKey: String?, filterableType: FilterableType) {
this.primaryKey = primaryKey
this.filterableType = filterableType
}
/** /**
* Update the most used filters visibility * Update the most used filters visibility
*/ */

@ -0,0 +1,112 @@
package net.pokeranalytics.android.ui.fragment
import android.app.Activity
import android.content.Context
import android.content.Intent
import io.realm.RealmResults
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.FiltersActivity
import net.pokeranalytics.android.ui.interfaces.FilterHandler.Companion.INTENT_FILTER_UPDATE_FILTER_UI
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.Preferences
import timber.log.Timber
open class FiltersListFragment : DataListFragment() {
private var identifiableClass: Class<out Deletable> = Filter::class.java
private var dataType: LiveData = LiveData.FILTER
private lateinit var items: RealmResults<Filter>
/**
* Set fragment data
*/
override fun setData(dataType: Int) {
super.setData(dataType)
this.dataType = LiveData.FILTER
this.identifiableClass = Filter::class.java
setToolbarTitle(this.dataType.pluralLocalizedTitle(requireContext()))
this.items = this.retrieveItems(getRealm()) as RealmResults<Filter>
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable? {
Timber.d("rowRepresentableForPosition: ${this.items[position] as RowRepresentable}")
return this.items[position] as RowRepresentable
}
override fun numberOfRows(): Int {
return this.items.size
}
override fun adapterRows(): List<RowRepresentable>? {
return items
}
override fun viewTypeForPosition(position: Int): Int {
val viewType = (this.items[position] as RowRepresentable).viewType
return if (viewType != -1) viewType else RowViewType.DATA.ordinal
}
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
when (row) {
is Filter -> {
row.updateValue(value, row)
dataListAdapter.refreshRow(row)
updateFilterUIIfNecessary(requireContext(), row.id)
}
else -> super.onRowValueChanged(value, row)
}
}
override fun onRowDeleted(row: RowRepresentable) {
when (row) {
is Filter -> {
val filterId = row.id
deleteItem(dataListAdapter, items, filterId)
if (filterId == Preferences.getActiveFilterId(requireContext())) {
Preferences.setActiveFilterId("", requireContext())
updateFilterUIIfNecessary(requireContext(), "")
}
}
else -> super.onRowDeleted(row)
}
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) {
is Filter -> {
if (fromAction) {
row.startEditing(null, this)
} else {
val intent = Intent()
intent.putExtra(FiltersActivity.IntentKey.FILTER_ID.keyName, row.id)
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
}
}
else -> {
val identifier = (row as Identifiable).id
EditableDataActivity.newInstanceForResult(this, this.dataType, identifier, REQUEST_CODE_DETAILS)
}
}
}
/**
* Update filter UI
*/
fun updateFilterUIIfNecessary(context: Context, filterId: String) {
if (filterId == Preferences.getActiveFilterId(context)) {
// Send broadcast
val intent = Intent()
intent.action = INTENT_FILTER_UPDATE_FILTER_UI
context.sendBroadcast(intent)
}
}
}

@ -14,13 +14,15 @@ import com.github.mikephil.charting.listener.OnChartValueSelectedListener
import kotlinx.android.synthetic.main.fragment_graph.* import kotlinx.android.synthetic.main.fragment_graph.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.interfaces.ObjectIdentifier
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.graph.AxisFormatting import net.pokeranalytics.android.ui.graph.AxisFormatting
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.graph.ObjectIdentifier
import net.pokeranalytics.android.ui.graph.setStyle import net.pokeranalytics.android.ui.graph.setStyle
import net.pokeranalytics.android.ui.view.LegendView import net.pokeranalytics.android.ui.view.LegendView
import net.pokeranalytics.android.ui.view.MultiLineLegendView import net.pokeranalytics.android.ui.view.MultiLineLegendView
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
class GraphFragment : RealmFragment(), OnChartValueSelectedListener { class GraphFragment : RealmFragment(), OnChartValueSelectedListener {
@ -150,8 +152,10 @@ class GraphFragment : RealmFragment(), OnChartValueSelectedListener {
barChart.data = barData barChart.data = barData
dataSets.firstOrNull()?.let { dataSets.firstOrNull()?.let {
val entry = it.getEntryForIndex(0) if (it.entryCount > 0) {
this.chartView?.highlightValue(entry.x, 0) val entry = it.getEntryForIndex(0)
this.chartView?.highlightValue(entry.x, 0)
}
} }
} }
@ -179,18 +183,23 @@ class GraphFragment : RealmFragment(), OnChartValueSelectedListener {
val statEntry = when (entry.data) { val statEntry = when (entry.data) {
is ObjectIdentifier -> { is ObjectIdentifier -> {
val identifier = entry.data as ObjectIdentifier val identifier = entry.data as ObjectIdentifier
getRealm().where(identifier.clazz).equalTo("id", identifier.id).findAll().firstOrNull() getRealm().findById(identifier.clazz, identifier.id)
} }
is GraphUnderlyingEntry -> entry.data as GraphUnderlyingEntry? is GraphUnderlyingEntry -> entry.data
else -> null else -> null
} }
statEntry?.let { if (statEntry is GraphUnderlyingEntry) {
val groupName = dataSet?.label ?: "" val groupName = dataSet?.label ?: ""
val color = dataSet?.color val color = dataSet?.color
val legendValue = it.legendValues(stat, entry, this.style, groupName, requireContext()) val legendValue = statEntry.legendValues(stat, entry, this.style, groupName, requireContext())
this.legendView.setItemData(legendValue, color) this.legendView.setItemData(legendValue, color)
} else {
Timber.w("Data $statEntry should implement GraphUnderlyingEntry")
} }
} }
} }

@ -4,27 +4,30 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_import.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.ResultCode
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.util.csv.CSVImporter import net.pokeranalytics.android.util.csv.CSVImporter
import net.pokeranalytics.android.util.csv.ImportDelegate
import net.pokeranalytics.android.util.csv.ImportException import net.pokeranalytics.android.util.csv.ImportException
import timber.log.Timber import timber.log.Timber
import java.io.InputStream import java.io.InputStream
import java.text.NumberFormat
import java.util.* import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class ImportFragment : RealmFragment() { class ImportFragment : RealmFragment(), ImportDelegate {
val coroutineContext: CoroutineContext val coroutineContext: CoroutineContext
get() = Dispatchers.Main get() = Dispatchers.Main
private lateinit var filePath: String private lateinit var filePath: String
private lateinit var inputStream: InputStream private lateinit var inputStream: InputStream
private lateinit var importer: CSVImporter
fun setData(path: String) { fun setData(path: String) {
this.filePath = path this.filePath = path
@ -42,12 +45,34 @@ class ImportFragment : RealmFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
this.initUI()
this.startImport() this.startImport()
} }
fun startImport() { private fun initUI() {
this.imported.text = requireContext().getString(R.string.imported)
this.total.text = requireContext().getString(R.string.total)
this.save.isEnabled = false
this.save.setOnClickListener {
this.end()
}
this.cancel.setOnClickListener {
this.cancel()
this.end()
}
}
private fun startImport() {
var shouldDismissActivity = false // var shouldDismissActivity = false
this.importer = CSVImporter(inputStream)
this.importer.delegate = this
GlobalScope.launch(coroutineContext) { GlobalScope.launch(coroutineContext) {
@ -56,10 +81,9 @@ class ImportFragment : RealmFragment() {
Timber.d(">>> Start Import...") Timber.d(">>> Start Import...")
try { try {
val csv = CSVImporter(inputStream) importer.start()
csv.start()
} catch (e: ImportException) { } catch (e: ImportException) {
shouldDismissActivity = true // shouldDismissActivity = true
} }
val e = Date() val e = Date()
val duration = (e.time - s.time) / 1000.0 val duration = (e.time - s.time) / 1000.0
@ -68,17 +92,42 @@ class ImportFragment : RealmFragment() {
} }
test.await() test.await()
if (shouldDismissActivity) { // if (shouldDismissActivity) {
//
// activity?.let {
// it.setResult(ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value)
// it.finish()
// }
//
// } else {
// }
importDidFinish()
activity?.let { }
it.setResult(ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value)
it.finish()
}
} }
} private fun cancel() {
this.importer.cancel(getRealm())
}
private fun importDidFinish() {
this.save.isEnabled = true
}
private fun end() {
activity?.finish()
}
val numberFormatter = NumberFormat.getNumberInstance()
// ImportDelegate
override fun parsingCountUpdate(importedCount: Int, totalCount: Int) {
this.counter.text = this.numberFormatter.format(importedCount)
this.totalCounter.text = this.numberFormatter.format(totalCount)
} }
} }

@ -9,6 +9,7 @@ import kotlinx.android.synthetic.main.fragment_more.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.BankrollActivity import net.pokeranalytics.android.ui.activity.BankrollActivity
import net.pokeranalytics.android.ui.activity.SettingsActivity import net.pokeranalytics.android.ui.activity.SettingsActivity
import net.pokeranalytics.android.ui.activity.Top10Activity
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
@ -62,6 +63,7 @@ class MoreFragment : PokerAnalyticsFragment(), StaticRowRepresentableDataSource,
super.onRowSelected(position, row, fromAction) super.onRowSelected(position, row, fromAction)
when(row) { when(row) {
MoreTabRow.BANKROLL -> BankrollActivity.newInstance(requireContext()) MoreTabRow.BANKROLL -> BankrollActivity.newInstance(requireContext())
MoreTabRow.TOP_10 -> Top10Activity.newInstance(requireContext())
MoreTabRow.SETTINGS -> SettingsActivity.newInstance(requireContext()) MoreTabRow.SETTINGS -> SettingsActivity.newInstance(requireContext())
} }
} }

@ -10,6 +10,8 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import kotlinx.android.synthetic.main.fragment_session.* import kotlinx.android.synthetic.main.fragment_session.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.extensions.SessionState import net.pokeranalytics.android.model.extensions.SessionState
import net.pokeranalytics.android.model.extensions.getState import net.pokeranalytics.android.model.extensions.getState
@ -21,8 +23,6 @@ import net.pokeranalytics.android.ui.activity.EditableDataActivity
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.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.helpers.DateTimePickerManager
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
@ -59,28 +59,41 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
/** /**
* Set fragment data * Set fragment data
*/ */
fun setData(isTournament: Boolean, sessionId: String) { fun setData(isTournament: Boolean, sessionId: String? = null, duplicate: Boolean) {
val realm = getRealm() val realm = getRealm()
val sessionRealm = realm.findById<Session>(sessionId) if (sessionId != null) {
if (sessionRealm != null) {
currentSession = sessionRealm val sessionRealm = realm.findById<Session>(sessionId)
sessionHasBeenCustomized = true if (sessionRealm != null) {
} else {
realm.beginTransaction() if (duplicate) { // duplicate session
currentSession = Session.newInstance(realm, isTournament) realm.executeTransaction {
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext()) val session = sessionRealm.duplicate()
realm.commitTransaction() currentSession = session
}
sessionHasBeenCustomized = false
} else { // show existing session
currentSession = sessionRealm
sessionHasBeenCustomized = true
}
} else {
throw PAIllegalStateException("Session cannot be null here, session id = $sessionId")
}
} else { // buildAndShow new session
realm.executeTransaction { executeRealm ->
currentSession = Session.newInstance(executeRealm, isTournament)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext())
}
// Find the nearest location around the user // Find the nearest location around the user
parentActivity?.findNearestLocation { parentActivity?.findNearestLocation {
it?.let { location -> it?.let { location ->
realm.beginTransaction() realm.executeTransaction { executeRealm ->
val realmLocation = realm.findById<Location>(location.id) val realmLocation = executeRealm.findById<Location>(location.id)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext()) FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext())
currentSession.location = realmLocation currentSession.location = realmLocation
realm.commitTransaction() }
updateSessionUI(true) updateSessionUI(true)
} }
} }
@ -143,37 +156,14 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
Toast.makeText(requireContext(), "Action for row: $row", Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), "Action for row: $row", Toast.LENGTH_SHORT).show()
return return
} }
row.startEditing(currentSession, this)
val data = currentSession.editDescriptors(row)
when (row) {
SessionRow.START_DATE -> DateTimePickerManager.create(requireContext(), row, this, currentSession.startDate)
SessionRow.END_DATE -> {
if (this.currentSession.startDate == null) {
Toast.makeText(context, R.string.session_missing_start_date, Toast.LENGTH_SHORT).show()
} else {
DateTimePickerManager.create(
requireContext(),
row,
this,
currentSession.endDate ?: currentSession.startDate ?: Date(),
currentSession.startDate
)
}
}
SessionRow.BANKROLL -> {
BottomSheetFragment.create(fragmentManager, row, this, data, false, currentSession.currency)
}
else -> BottomSheetFragment.create(fragmentManager, row, this, data, currentCurrency = currentSession.currency)
}
} }
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
sessionHasBeenCustomized = true sessionHasBeenCustomized = true
try { try {
currentSession.updateValue(value, row) currentSession.updateValue(value, row)
} catch (e: IllegalStateException) { } catch (e: PAIllegalStateException) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
return return
} }
@ -345,6 +335,9 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
* Delete a session * Delete a session
*/ */
private fun deleteSession() { private fun deleteSession() {
currentSession.bankroll?.id?.let { id ->
BankrollReportManager.notifyBankrollReportImpact(id)
}
currentSession.delete() currentSession.delete()
activity?.finish() activity?.finish()
} }

@ -13,11 +13,10 @@ import io.realm.Realm
import kotlinx.android.synthetic.main.fragment_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
import net.pokeranalytics.android.BuildConfig import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Currency
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.activity.BillingActivity import net.pokeranalytics.android.ui.activity.*
import net.pokeranalytics.android.ui.activity.CurrenciesActivity import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.GDPRActivity
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
@ -57,7 +56,6 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
rows rows
} }
const val REQUEST_CODE_CURRENCY: Int = 100
} }
private lateinit var settingsAdapterRow: RowRepresentableAdapter private lateinit var settingsAdapterRow: RowRepresentableAdapter
@ -73,17 +71,29 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_CURRENCY && resultCode == Activity.RESULT_OK) {
data?.let { when (requestCode) {
Preferences.setCurrencyCode(data.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE), requireContext()) RequestCode.CURRENCY.value -> {
val realm = Realm.getDefaultInstance() if (resultCode == Activity.RESULT_OK) {
realm.executeTransaction { data?.let {
it.where(Session::class.java).isNull("bankroll.currency.code").findAll().forEach { Preferences.setCurrencyCode(data.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE), requireContext())
it.bankrollHasBeenUpdated() val realm = Realm.getDefaultInstance()
} realm.executeTransaction {
} realm.where(Currency::class.java).isNull("code").or().equalTo("code", UserDefaults.currency.currencyCode).findAll().forEach { currency ->
realm.close() currency.rate = Currency.DEFAULT_RATE
settingsAdapterRow.refreshRow(SettingRow.CURRENCY) }
realm.where(Session::class.java).isNull("bankroll.currency.code").findAll().forEach { session ->
session.bankrollHasBeenUpdated()
}
}
realm.close()
settingsAdapterRow.refreshRow(SettingRow.CURRENCY)
}
}
}
RequestCode.SUBSCRIPTION.value -> {
settingsAdapterRow.refreshRow(SettingRow.SUBSCRIPTION)
} }
} }
} }
@ -103,9 +113,11 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) { when (row) {
SettingRow.BANKROLL_REPORT -> BankrollActivity.newInstance(requireContext())
SettingRow.TOP_10 -> Top10Activity.newInstance(requireContext())
SettingRow.SUBSCRIPTION -> { SettingRow.SUBSCRIPTION -> {
if (!AppGuard.isProUser) { if (!AppGuard.isProUser) {
BillingActivity.newInstance(requireContext()) BillingActivity.newInstanceForResult(this, false)
} else { } else {
this.openPlaystoreAccount() this.openPlaystoreAccount()
} }
@ -113,7 +125,7 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
SettingRow.RATE_APP -> parentActivity?.openPlayStorePage() SettingRow.RATE_APP -> parentActivity?.openPlayStorePage()
SettingRow.CONTACT_US -> parentActivity?.openContactMail(R.string.contact) SettingRow.CONTACT_US -> parentActivity?.openContactMail(R.string.contact)
SettingRow.BUG_REPORT -> parentActivity?.openContactMail(R.string.bug_report_subject, Realm.getDefaultInstance().path) SettingRow.BUG_REPORT -> parentActivity?.openContactMail(R.string.bug_report_subject, Realm.getDefaultInstance().path)
SettingRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@SettingsFragment, REQUEST_CODE_CURRENCY) SettingRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@SettingsFragment, RequestCode.CURRENCY.value)
SettingRow.FOLLOW_US -> { SettingRow.FOLLOW_US -> {
when (position) { when (position) {
0 -> parentActivity?.openUrl(URL.BLOG.value) 0 -> parentActivity?.openUrl(URL.BLOG.value)
@ -137,6 +149,8 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
*/ */
private fun initUI() { private fun initUI() {
setToolbarTitle(getString(R.string.more))
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
val viewManager = LinearLayoutManager(requireContext()) val viewManager = LinearLayoutManager(requireContext())

@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel import io.realm.RealmModel
import io.realm.RealmResults
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -32,7 +33,7 @@ import kotlin.coroutines.CoroutineContext
class StatisticsFragment : FilterableFragment() { class StatisticsFragment : FilterableFragment() {
val coroutineContext: CoroutineContext private val coroutineContext: CoroutineContext
get() = Dispatchers.Main get() = Dispatchers.Main
private lateinit var tableReportFragment: ComposableTableReportFragment private lateinit var tableReportFragment: ComposableTableReportFragment
@ -74,7 +75,7 @@ class StatisticsFragment : FilterableFragment() {
override val observedEntities: List<Class<out RealmModel>> = listOf(ComputableResult::class.java) override val observedEntities: List<Class<out RealmModel>> = listOf(ComputableResult::class.java)
override fun entitiesChanged(clazz: Class<out RealmModel>) { override fun entitiesChanged(clazz: Class<out RealmModel>, results: RealmResults<out RealmModel>) {
this.launchStatComputation() this.launchStatComputation()
} }

@ -1,6 +1,9 @@
package net.pokeranalytics.android.ui.fragment package net.pokeranalytics.android.ui.fragment
import android.content.Context
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
@ -24,6 +27,7 @@ import com.android.billingclient.api.SkuDetails
import com.android.billingclient.api.SkuDetailsResponseListener import com.android.billingclient.api.SkuDetailsResponseListener
import kotlinx.android.synthetic.main.fragment_subscription.* import kotlinx.android.synthetic.main.fragment_subscription.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.fragment.components.ScreenSlidePageFragment import net.pokeranalytics.android.ui.fragment.components.ScreenSlidePageFragment
@ -42,10 +46,20 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
private var pagerAdapter: ScreenSlidePagerAdapter? = null private var pagerAdapter: ScreenSlidePagerAdapter? = null
private var selectedProduct: SkuDetails? = null private var selectedProduct: SkuDetails? = null
private var showSessionMessage = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val cm = requireContext().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork: NetworkInfo? = cm.activeNetworkInfo
val isConnected: Boolean = activeNetwork?.isConnected == true
if (!isConnected) {
Toast.makeText(requireContext(), R.string.billingclient_unavailable, Toast.LENGTH_LONG).show()
return
}
this.showLoader(R.string.loading_please_wait) this.showLoader(R.string.loading_please_wait)
if (!AppGuard.requestProducts(this)) { if (!AppGuard.requestProducts(this)) {
this.hideLoader() this.hideLoader()
@ -53,6 +67,10 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
} }
} }
fun setData(showSessionMessage: Boolean) {
this.showSessionMessage = showSessionMessage
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_subscription, container, false) return inflater.inflate(R.layout.fragment_subscription, container, false)
} }
@ -88,6 +106,10 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
this.title.text = upgradeString this.title.text = upgradeString
} }
if (showSessionMessage) {
this.message.text = getString(R.string.iap_session_message)
}
// 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.
@ -101,7 +123,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
this.selectedProduct?.let { this.selectedProduct?.let {
AppGuard.initiatePurchase(this.requireActivity(), it, this) AppGuard.initiatePurchase(this.requireActivity(), it, this)
} ?: run { } ?: run {
throw IllegalStateException("Attempt to initiate purchase while no product has been chosen") throw PAIllegalStateException("Attempt to initiate purchase while no product has been chosen")
} }
} }
@ -109,7 +131,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
for (i in 1..count) { for (i in 1..count) {
val view = View(requireContext()) val view = View(requireContext())
view.background = requireContext().getDrawable(R.drawable.circle_green) view.background = requireContext().getDrawable(R.drawable.circle_green)
val layoutParam = LinearLayout.LayoutParams(10.px, 10.px) val layoutParam = LinearLayout.LayoutParams(8.px, 8.px)
layoutParam.setMargins(6.px) layoutParam.setMargins(6.px)
this.pageIndicator.addView(view, layoutParam) this.pageIndicator.addView(view, layoutParam)
} }
@ -201,12 +223,8 @@ 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 { pagerAdapter?.getFragment(position)?.updateViewsPosition(-positionOffset * parallax)
it.updateViewsPosition(-positionOffset * parallax) pagerAdapter?.getFragment(position + 1)?.updateViewsPosition((1 - positionOffset) * parallax)
}
pagerAdapter?.getFragment(position + 1)?.let {
it.updateViewsPosition((1 - positionOffset) * parallax)
}
} }
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {

@ -0,0 +1,175 @@
package net.pokeranalytics.android.ui.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.tabs.TabLayout
import io.realm.RealmResults
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_top_10.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
class Top10Fragment : RealmFragment(), RowRepresentableDataSource, RowRepresentableDelegate {
private enum class Tab {
CASH_GAMES,
TOURNAMENTS
}
companion object {
fun newInstance(): Top10Fragment {
val fragment = Top10Fragment()
val bundle = Bundle()
fragment.arguments = bundle
return fragment
}
}
private lateinit var positiveSessions: RealmResults<Session>
private lateinit var dataListAdapter: RowRepresentableAdapter
private var realmCashGames: List<Session> = mutableListOf()
private var realmTournaments: List<Session> = mutableListOf()
private var currentTab: Tab = Tab.CASH_GAMES
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_top_10, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
initData()
}
/**
* Init UI
*/
private fun initUI() {
dataListAdapter = RowRepresentableAdapter(this, this)
recyclerView.adapter = dataListAdapter
setDisplayHomeAsUpEnabled(true)
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) {
0 -> {
currentTab = Tab.CASH_GAMES
dataListAdapter.notifyDataSetChanged()
}
1 -> {
currentTab = Tab.TOURNAMENTS
}
}
dataListAdapter.notifyDataSetChanged()
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
val viewManager = SmoothScrollLinearLayoutManager(requireContext())
recyclerView.apply {
setHasFixedSize(true)
layoutManager = viewManager
}
}
/**
* Init data
*/
private fun initData() {
this.positiveSessions = getRealm().where<Session>()
.isNotNull("endDate")
.greaterThan("result.net", 0.0)
.findAll()
updateTop10()
}
private fun updateTop10() {
val cashGames = mutableListOf<Session>()
val tournaments = mutableListOf<Session>()
// filter by type: cash game or tournament
this.positiveSessions.forEach {
when (it.type) {
Session.Type.CASH_GAME.ordinal -> {
cashGames.add(it)
}
else -> {
tournaments.add(it)
}
}
}
// Sort by rated net
val sortedCashGames = cashGames.sortedByDescending {
it.computableResult?.ratedNet
}.toMutableList()
val sortedTournaments = tournaments.sortedByDescending {
it.computableResult?.ratedNet
}.toMutableList()
// Keep 10 items
if (sortedCashGames.size > 10) {
sortedCashGames.subList(10, sortedCashGames.size).clear()
}
if (sortedTournaments.size > 10) {
sortedTournaments.subList(10, sortedTournaments.size).clear()
}
this.realmCashGames = sortedCashGames
this.realmTournaments = sortedTournaments
dataListAdapter.notifyDataSetChanged()
}
override fun adapterRows(): List<RowRepresentable>? {
return when (currentTab) {
Tab.CASH_GAMES -> realmCashGames
Tab.TOURNAMENTS -> realmTournaments
}
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable? {
return when (currentTab) {
Tab.CASH_GAMES -> realmCashGames[position]
Tab.TOURNAMENTS -> realmTournaments[position]
}
}
override fun numberOfRows(): Int {
return when (currentTab) {
Tab.CASH_GAMES -> realmCashGames.size
Tab.TOURNAMENTS -> realmTournaments.size
}
}
override fun viewTypeForPosition(position: Int): Int {
return RowViewType.ROW_TOP_10.ordinal
}
}

@ -13,6 +13,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.ui.activity.DataListActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
@ -77,7 +78,7 @@ abstract class DeletableItemFragment : RealmFragment() {
this.dataListAdapter = dataListAdapter this.dataListAdapter = dataListAdapter
// Save the delete position & create a copy of the object // Save the delete position & buildAndShow a copy of the object
val itemPosition = items.indexOfFirst { it.id == itemId } val itemPosition = items.indexOfFirst { it.id == itemId }
val itemToDelete = items.find { it.id == itemId } val itemToDelete = items.find { it.id == itemId }
@ -88,11 +89,11 @@ abstract class DeletableItemFragment : RealmFragment() {
deletedItem = getRealm().copyFromRealm(itemToDelete) deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition lastDeletedItemPosition = itemPosition
getRealm().executeTransaction { getRealm().executeTransaction {
itemToDelete.deleteDependencies() itemToDelete.deleteDependencies(it)
itemToDelete.deleteFromRealm() itemToDelete.deleteFromRealm()
} }
itemHasBeenReInserted = false itemHasBeenReInserted = false
updateUIAfterDeletion(itemPosition) updateUIAfterDeletion(itemId, itemPosition)
showUndoSnackBar() showUndoSnackBar()
} else { } else {
dataListAdapter.notifyItemChanged(itemPosition) dataListAdapter.notifyItemChanged(itemPosition)
@ -126,14 +127,14 @@ abstract class DeletableItemFragment : RealmFragment() {
} }
snackBar?.show() snackBar?.show()
} ?: run { } ?: run {
throw IllegalStateException("mainLayout is not defined") throw PAIllegalStateException("mainLayout is not defined")
} }
} }
/** /**
* Called once the object has been deleted * Called once the object has been deleted
*/ */
open fun updateUIAfterDeletion(itemPosition: Int) { open fun updateUIAfterDeletion(itemId: String, itemPosition: Int) {
dataListAdapter.notifyItemRemoved(itemPosition) dataListAdapter.notifyItemRemoved(itemPosition)
} }

@ -7,6 +7,7 @@ import android.view.ViewGroup
import io.realm.Realm import io.realm.Realm
import io.realm.RealmModel import io.realm.RealmModel
import io.realm.RealmResults import io.realm.RealmResults
import timber.log.Timber
open class RealmFragment : PokerAnalyticsFragment() { open class RealmFragment : PokerAnalyticsFragment() {
@ -26,8 +27,8 @@ open class RealmFragment : PokerAnalyticsFragment() {
this.observedEntities.forEach { this.observedEntities.forEach {
val realmResults = realm.where(it).findAll() val realmResults = realm.where(it).findAll()
realmResults.addChangeListener { _,_ -> realmResults.addChangeListener { t, _ ->
this.entitiesChanged(it) this.entitiesChanged(it, t)
} }
this.observedRealmResults.add(realmResults) this.observedRealmResults.add(realmResults)
@ -61,6 +62,6 @@ open class RealmFragment : PokerAnalyticsFragment() {
/** /**
* The method called when a change happened in any RealmResults * The method called when a change happened in any RealmResults
*/ */
open fun entitiesChanged(clazz: Class<out RealmModel>) {} open fun entitiesChanged(clazz: Class<out RealmModel>, results: RealmResults<out RealmModel>) {}
} }

@ -1,48 +0,0 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet
enum class BottomSheetType {
NONE,
LIST,
LIST_STATIC,
LIST_GAME,
DOUBLE_LIST,
MULTI_SELECTION,
GRID,
EDIT_TEXT,
EDIT_TEXT_MULTI_LINES,
DOUBLE_EDIT_TEXT,
NUMERIC_TEXT,
SUM;
fun newInstance(): BottomSheetFragment {
return when (this) {
NONE -> BottomSheetFragment()
LIST -> BottomSheetListFragment()
LIST_STATIC -> BottomSheetStaticListFragment()
LIST_GAME -> BottomSheetListGameFragment()
DOUBLE_LIST -> BottomSheetListGameFragment()
MULTI_SELECTION -> BottomSheetMultiSelectionFragment()
GRID -> BottomSheetTableSizeGridFragment()
EDIT_TEXT -> BottomSheetEditTextFragment()
EDIT_TEXT_MULTI_LINES -> BottomSheetEditTextMultiLinesFragment()
DOUBLE_EDIT_TEXT -> BottomSheetDoubleEditTextFragment()
NUMERIC_TEXT -> BottomSheetNumericTextFragment()
SUM -> BottomSheetSumFragment()
}
}
val validationRequired: Boolean
get() = when (this) {
LIST, LIST_GAME, LIST_STATIC, GRID, DOUBLE_LIST -> false
else -> true
}
val clearRequired: Boolean
get() = true
val addRequired: Boolean
get() = when (this) {
EDIT_TEXT, NUMERIC_TEXT, DOUBLE_EDIT_TEXT, EDIT_TEXT_MULTI_LINES, GRID, LIST_STATIC, SUM -> false
else -> true
}
}

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
@ -10,11 +10,12 @@ import kotlinx.android.synthetic.main.bottom_sheet_double_edit_text.*
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.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.util.extensions.round import net.pokeranalytics.android.util.extensions.round
class BottomSheetDoubleEditTextFragment : BottomSheetFragment() { class InputDoubleEditTextFragment(row: RowRepresentable) : InputFragment(row) {
private val values = ArrayList<String>() private val values = ArrayList<String>()
private var isEditingBlinds: Boolean = false private var isEditingBlinds: Boolean = false
@ -30,11 +31,13 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
if (isEditingBlinds) { if (isEditingBlinds) {
editText2.requestFocus() editText2.requestFocus()
} else { } else {
editText1.requestFocus() editText.requestFocus()
} }
} }
override fun getValue(): Any? { override fun getValue(): Any? {
if (values.isEmpty()) { return null }
if (values.all { it.isEmpty() }) { return null }
return values return values
} }
@ -42,7 +45,6 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
valueHasPlaceholder = true
isEditingBlinds = row == SessionRow.BLINDS isEditingBlinds = row == SessionRow.BLINDS
} }
@ -50,9 +52,9 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val data = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (data.size != 2) { if (data.size != 2) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
values.add(0, "") values.add(0, "")
@ -64,20 +66,20 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
values[0] = (data[0].defaultValue ?: "").toString() values[0] = (data[0].defaultValue ?: "").toString()
values[1] = (data[1].defaultValue ?: "").toString() values[1] = (data[1].defaultValue ?: "").toString()
data[0].hint?.let { editText1.hint = getString(it) } data[0].hint?.let { editText.hint = getString(it) }
editText1.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES editText.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
data[1].hint?.let { editText2.hint = getString(it) } data[1].hint?.let { editText2.hint = getString(it) }
editText2.inputType = data[1].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES editText2.inputType = data[1].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
if (valueHasPlaceholder) { if (valueHasPlaceholder) {
editText1.hint = values[0] if (values[0].isNotBlank()) { editText.hint = values[0] }
editText2.hint = values[1] if (values[1].isNotBlank()) { editText2.hint = values[1] }
} else { } else {
editText1.setText(values[0]) editText.setText(values[0])
editText2.setText(values[1]) editText2.setText(values[1])
} }
editText1.addTextChangedListener { editText.addTextChangedListener {
values[0] = it?.toString() ?: "" values[0] = it?.toString() ?: ""
} }
@ -86,9 +88,9 @@ class BottomSheetDoubleEditTextFragment : BottomSheetFragment() {
if (isEditingBlinds) { if (isEditingBlinds) {
try { try {
val bigBlind = values[1].toDouble() val bigBlind = values[1].toDouble()
editText1.setText((bigBlind / 2.0).round()) editText.setText((bigBlind / 2.0).round())
} catch (e: Exception) { } catch (e: Exception) {
editText1.setText("") editText.setText("")
} }
} }
} }

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
@ -10,9 +10,10 @@ import kotlinx.android.synthetic.main.bottom_sheet_edit_text.*
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.ui.view.RowRepresentable
class BottomSheetEditTextFragment : BottomSheetFragment() { class InputEditTextFragment(row: RowRepresentable) : InputFragment(row) {
private var value: String? = null private var value: String? = null
@ -24,7 +25,7 @@ class BottomSheetEditTextFragment : BottomSheetFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
editText1.requestFocus() editText.requestFocus()
} }
override fun getValue(): Any? { override fun getValue(): Any? {
@ -43,7 +44,6 @@ class BottomSheetEditTextFragment : BottomSheetFragment() {
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
valueHasPlaceholder = true
} }
/** /**
@ -51,28 +51,28 @@ class BottomSheetEditTextFragment : BottomSheetFragment() {
*/ */
private fun initUI() { private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val data = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (data.size != 1) { if (data.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_edit_text, view?.bottomSheetContainer, true) LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_edit_text, view?.bottomSheetContainer, true)
data[0].hint?.let { editText1.hint = getString(it) } data[0].hint?.let { editText.hint = getString(it) }
editText1.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES editText.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
editText1.addTextChangedListener { editText.addTextChangedListener {
this.value = it?.toString() this.value = it?.toString()
} }
data[0].defaultValue?.let { data[0].defaultValue?.let {
if (valueHasPlaceholder) { if (valueHasPlaceholder) {
this.value = it.toString() this.value = it.toString()
editText1.hint = it.toString() editText.hint = it.toString()
} else { } else {
editText1.setText(it.toString()) editText.setText(it.toString())
} }
} }
editText1.setOnEditorActionListener { _, actionId, _ -> editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) { if (actionId == EditorInfo.IME_ACTION_DONE) {
delegate.onRowValueChanged(getValue(), row) delegate.onRowValueChanged(getValue(), row)
dismiss() dismiss()

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
@ -8,9 +8,10 @@ import androidx.core.widget.addTextChangedListener
import kotlinx.android.synthetic.main.bottom_sheet_edit_text_multi_lines.* import kotlinx.android.synthetic.main.bottom_sheet_edit_text_multi_lines.*
import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.* import kotlinx.android.synthetic.main.fragment_bottom_sheet.view.*
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
import net.pokeranalytics.android.ui.view.RowRepresentable
class BottomSheetEditTextMultiLinesFragment : BottomSheetFragment() { class InputEditTextMultiLinesFragment(row: RowRepresentable) : InputFragment(row) {
private var value: String? = null private var value: String? = null
@ -22,7 +23,7 @@ class BottomSheetEditTextMultiLinesFragment : BottomSheetFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
editText1.requestFocus() editText.requestFocus()
} }
override fun getValue(): Any? { override fun getValue(): Any? {
@ -42,18 +43,18 @@ class BottomSheetEditTextMultiLinesFragment : BottomSheetFragment() {
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val data = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (data.size != 1) { if (data.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
LayoutInflater.from(requireContext()).inflate(net.pokeranalytics.android.R.layout.bottom_sheet_edit_text_multi_lines, view?.bottomSheetContainer, true) LayoutInflater.from(requireContext()).inflate(net.pokeranalytics.android.R.layout.bottom_sheet_edit_text_multi_lines, view?.bottomSheetContainer, true)
data[0].hint?.let { editText1.hint = getString(it) } data[0].hint?.let { editText.hint = getString(it) }
editText1.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES editText.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
editText1.addTextChangedListener { this.value = it?.toString() } editText.addTextChangedListener { this.value = it?.toString() }
data[0].defaultValue?.let { data[0].defaultValue?.let {
editText1.setText(it.toString()) editText.setText(it.toString())
} }
} }

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
@ -10,57 +10,59 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.fragment.app.FragmentManager import androidx.fragment.app.Fragment
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import io.realm.RealmModel import io.realm.RealmModel
import kotlinx.android.synthetic.main.fragment_bottom_sheet.* import kotlinx.android.synthetic.main.fragment_bottom_sheet.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.exceptions.PokerAnalyticsException
import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowEditableDelegate
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.helpers.DateTimePickerManager
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.*
import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionRow import net.pokeranalytics.android.ui.view.rowrepresentable.TransactionRow
import java.util.* import java.util.*
open class BottomSheetFragment : BottomSheetDialogFragment() { open class InputFragment(val row: RowRepresentable) : BottomSheetDialogFragment() {
lateinit var row: RowRepresentable // lateinit var row: RowRepresentable
lateinit var delegate: RowRepresentableDelegate lateinit var delegate: RowEditableDelegate
var currentCurrency: Currency? = null var currentCurrency: Currency? = null
var valueHasPlaceholder: Boolean = false var valueHasPlaceholder: Boolean = false
private var isClearable: Boolean = true private var isClearable: Boolean = true
private var isDeletable: Boolean = false private var isDeletable: Boolean = false
private var rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>? = null private var rowEditableDescriptors: ArrayList<RowEditableDescriptor>? = null
companion object { companion object {
const val REQUEST_CODE_ADD_NEW_OBJECT = 100 const val REQUEST_CODE_ADD_NEW_OBJECT = 100
fun create( fun buildAndShow(
fragmentManager: FragmentManager?, row: RowRepresentable,
row: RowRepresentable, delegate: RowEditableDelegate,
delegate: RowRepresentableDelegate, dataSource: RowEditableDataSource?,
rowRepresentableEditDescriptors: ArrayList<RowRepresentableEditDescriptor>?, isDeletable: Boolean? = false,
isClearable: Boolean? = true, valueHasPlaceholder: Boolean? = null
currentCurrency: Currency? = null, ) {
isDeletable: Boolean? = false, if (delegate !is Fragment) throw PokerAnalyticsException.InputFragmentException
valueHasPlaceholder: Boolean? = false if (dataSource?.descriptorType == RowEditableDescriptorType.DATE) {
): BottomSheetFragment { DateTimePickerManager.buildAndShow(row, delegate, dataSource.descriptors.first())
val bottomSheetFragment = row.bottomSheetType.newInstance() } else {
bottomSheetFragment.show(fragmentManager, "bottomSheet") val bottomSheetFragment = row.inputFragmentType.newInstance(row)
bottomSheetFragment.row = row bottomSheetFragment.show(delegate.fragmentManager, "bottomSheet")
bottomSheetFragment.delegate = delegate bottomSheetFragment.delegate = delegate
bottomSheetFragment.rowRepresentableEditDescriptors = rowRepresentableEditDescriptors bottomSheetFragment.rowEditableDescriptors = dataSource?.descriptors
bottomSheetFragment.isClearable = isClearable ?: true bottomSheetFragment.isClearable = row.valueCanBeClearedWhenEditing
bottomSheetFragment.isDeletable = isDeletable ?: true bottomSheetFragment.isDeletable = isDeletable ?: true
bottomSheetFragment.valueHasPlaceholder = valueHasPlaceholder ?: true bottomSheetFragment.valueHasPlaceholder = valueHasPlaceholder ?: true
bottomSheetFragment.currentCurrency = currentCurrency bottomSheetFragment.currentCurrency = dataSource?.currency
return bottomSheetFragment }
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -107,9 +109,9 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
bottomSheetToolbar.setOnMenuItemClickListener { bottomSheetToolbar.setOnMenuItemClickListener {
false false
} }
bottomSheetToolbar.menu.findItem(R.id.actionCheck).isVisible = row.bottomSheetType.validationRequired bottomSheetToolbar.menu.findItem(R.id.actionCheck).isVisible = row.inputFragmentType.validationRequired
bottomSheetToolbar.menu.findItem(R.id.actionClear).isVisible = row.bottomSheetType.clearRequired bottomSheetToolbar.menu.findItem(R.id.actionClear).isVisible = row.inputFragmentType.clearRequired
bottomSheetToolbar.menu.findItem(R.id.actionAdd).isVisible = row.bottomSheetType.addRequired bottomSheetToolbar.menu.findItem(R.id.actionAdd).isVisible = row.inputFragmentType.addRequired
// Menu // Menu
bottomSheetToolbar.menu.findItem(R.id.actionClear).setOnMenuItemClickListener { bottomSheetToolbar.menu.findItem(R.id.actionClear).setOnMenuItemClickListener {
@ -132,7 +134,7 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
SessionRow.TOURNAMENT_NAME -> LiveData.TOURNAMENT_NAME SessionRow.TOURNAMENT_NAME -> LiveData.TOURNAMENT_NAME
SessionRow.TOURNAMENT_FEATURE -> LiveData.TOURNAMENT_FEATURE SessionRow.TOURNAMENT_FEATURE -> LiveData.TOURNAMENT_FEATURE
TransactionRow.TYPE -> LiveData.TRANSACTION_TYPE TransactionRow.TYPE -> LiveData.TRANSACTION_TYPE
else -> throw IllegalStateException("row $row does not have an associated LiveData value") else -> throw PAIllegalStateException("row $row does not have an associated LiveData value")
} }
EditableDataActivity.newInstanceForResult( EditableDataActivity.newInstanceForResult(
@ -157,8 +159,8 @@ open class BottomSheetFragment : BottomSheetDialogFragment() {
/** /**
* Return the data list * Return the data list
*/ */
fun getData(): ArrayList<RowRepresentableEditDescriptor>? { fun getData(): ArrayList<RowEditableDescriptor>? {
return this.rowRepresentableEditDescriptors return this.rowEditableDescriptors
} }
open fun getValue(): Any? { open fun getValue(): Any? {

@ -0,0 +1,50 @@
package net.pokeranalytics.android.ui.fragment.components.input
import net.pokeranalytics.android.ui.view.RowRepresentable
enum class InputFragmentType {
NONE,
LIST,
LIST_STATIC,
LIST_GAME,
DOUBLE_LIST,
MULTI_SELECTION,
GRID,
EDIT_TEXT,
EDIT_TEXT_MULTI_LINES,
DOUBLE_EDIT_TEXT,
NUMERIC_TEXT,
SUM;
fun newInstance(row: RowRepresentable): InputFragment {
return when (this) {
NONE -> InputFragment(row)
LIST -> InputListFragment(row)
LIST_STATIC -> InputStaticListFragment(row)
LIST_GAME -> InputListGameFragment(row)
DOUBLE_LIST -> InputListGameFragment(row)
MULTI_SELECTION -> InputMultiSelectionFragment(row)
GRID -> InputTableSizeGridFragment(row)
EDIT_TEXT -> InputEditTextFragment(row)
EDIT_TEXT_MULTI_LINES -> InputEditTextMultiLinesFragment(row)
DOUBLE_EDIT_TEXT -> InputDoubleEditTextFragment(row)
NUMERIC_TEXT -> InputNumericTextFragment(row)
SUM -> InputSumFragment(row)
}
}
val validationRequired: Boolean
get() = when (this) {
LIST, LIST_GAME, LIST_STATIC, GRID, DOUBLE_LIST -> false
else -> true
}
val clearRequired: Boolean
get() = true
val addRequired: Boolean
get() = when (this) {
EDIT_TEXT, NUMERIC_TEXT, DOUBLE_EDIT_TEXT, EDIT_TEXT_MULTI_LINES, GRID, LIST_STATIC, SUM -> false
else -> true
}
}

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -8,14 +8,16 @@ import io.realm.RealmResults
import kotlinx.android.synthetic.main.bottom_sheet_list.* 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.PAIllegalStateException
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource
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.view.DataRowEditableDescriptor
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
open class BottomSheetListFragment : BottomSheetFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate { open class InputListFragment(row: RowRepresentable) : InputFragment(row), LiveRowRepresentableDataSource, RowRepresentableDelegate {
lateinit var dataAdapter: RowRepresentableAdapter lateinit var dataAdapter: RowRepresentableAdapter
var realmData: RealmResults<RowRepresentable>? = null var realmData: RealmResults<RowRepresentable>? = null
@ -39,14 +41,14 @@ open class BottomSheetListFragment : BottomSheetFragment(), LiveRowRepresentable
realmData?.let { realmData?.let {
return it[position] as RowRepresentable return it[position] as RowRepresentable
} }
throw IllegalStateException("Need to implement Data Source") throw PAIllegalStateException("Need to implement Data Source")
} }
override fun numberOfRows(): Int { override fun numberOfRows(): Int {
realmData?.let { realmData?.let {
return it.size return it.size
} }
throw IllegalStateException("Need to implement Data Source") throw PAIllegalStateException("Need to implement Data Source")
} }
override fun viewTypeForPosition(position: Int): Int { override fun viewTypeForPosition(position: Int): Int {
@ -68,14 +70,14 @@ open class BottomSheetListFragment : BottomSheetFragment(), LiveRowRepresentable
* Init data * Init data
*/ */
open fun initData() { open fun initData() {
val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (bottomSheetData.size != 1) { if (bottomSheetData.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
if (bottomSheetData.first().data == null) { val dataList = bottomSheetData[0]
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") if (dataList !is DataRowEditableDescriptor) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
} if (dataList.data == null) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
this.realmData = bottomSheetData.first().data as RealmResults<RowRepresentable> this.realmData = dataList.data
} }
/** /**

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -13,13 +13,14 @@ import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorExcep
import net.pokeranalytics.android.model.Limit import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.view.DataRowEditableDescriptor
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
/** /**
* Bottom Sheet List Game Fragment * Bottom Sheet List Game Fragment
* Display a list of game + chips to choose the game limit * Display a list of game + chips to choose the game limit
*/ */
class BottomSheetListGameFragment : BottomSheetListFragment() { class InputListGameFragment(row: RowRepresentable) : InputListFragment(row) {
private var limit: Int? = 0 private var limit: Int? = 0
private val values = ArrayList<Any?>() private val values = ArrayList<Any?>()
@ -49,15 +50,15 @@ class BottomSheetListGameFragment : BottomSheetListFragment() {
* Init data * Init data
*/ */
override fun initData() { override fun initData() {
val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (bottomSheetData.size != 2) { if (bottomSheetData.size != 2) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
}
if (bottomSheetData[1].data == null) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency")
} }
val dataList = bottomSheetData[1]
if (dataList !is DataRowEditableDescriptor) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
if (dataList.data == null) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
this.realmData = dataList.data
this.limit = bottomSheetData[0].defaultValue as Int? this.limit = bottomSheetData[0].defaultValue as Int?
this.realmData = bottomSheetData[1].data
} }
/** /**

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
@ -14,7 +14,7 @@ import net.pokeranalytics.android.ui.view.RowViewType
/** /**
* Manage multiple items selection in a bottom sheet list * Manage multiple items selection in a bottom sheet list
*/ */
open class BottomSheetMultiSelectionFragment : BottomSheetListFragment() { open class InputMultiSelectionFragment(row: RowRepresentable) : InputListFragment(row) {
private val selectedRows: ArrayList<RowRepresentable> = ArrayList() private val selectedRows: ArrayList<RowRepresentable> = ArrayList()
@ -54,9 +54,9 @@ open class BottomSheetMultiSelectionFragment : BottomSheetListFragment() {
override fun initData() { override fun initData() {
super.initData() super.initData()
val bottomSheetData = val bottomSheetData =
getData() ?: throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") getData() ?: throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (bottomSheetData.size != 1) { if (bottomSheetData.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
bottomSheetData.first().defaultValue?.let { bottomSheetData.first().defaultValue?.let {
(it as RealmList<*>).forEach { row -> (it as RealmList<*>).forEach { row ->

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
@ -10,10 +10,11 @@ import kotlinx.android.synthetic.main.bottom_sheet_edit_text.*
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.ui.view.RowRepresentable
import java.text.NumberFormat import java.text.NumberFormat
class BottomSheetNumericTextFragment : BottomSheetFragment() { class InputNumericTextFragment(row: RowRepresentable) : InputFragment(row) {
private var value: Double? = null private var value: Double? = null
@ -25,7 +26,7 @@ class BottomSheetNumericTextFragment : BottomSheetFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
editText1.requestFocus() editText.requestFocus()
} }
override fun getValue(): Any? { override fun getValue(): Any? {
@ -36,26 +37,25 @@ class BottomSheetNumericTextFragment : BottomSheetFragment() {
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
valueHasPlaceholder = true
} }
/** /**
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val data = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (data.size != 1) { if (data.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_edit_text, view?.bottomSheetContainer, true) LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_edit_text, view?.bottomSheetContainer, true)
data[0].hint?.let { editText1.hint = getString(it) } data[0].hint?.let { editText.hint = getString(it) }
editText1.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES editText.inputType = data[0].inputType ?: InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
editText1.addTextChangedListener { editText.addTextChangedListener {
this.value = try { this.value = try {
editText1.text.toString().toDouble() editText.text.toString().toDouble()
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
@ -73,13 +73,13 @@ class BottomSheetNumericTextFragment : BottomSheetFragment() {
if (valueHasPlaceholder) { if (valueHasPlaceholder) {
this.value = it.toString().toDoubleOrNull() this.value = it.toString().toDoubleOrNull()
editText1.hint = valueString editText.hint = valueString
} else { } else {
editText1.setText(valueString) editText.setText(valueString)
} }
} }
editText1.setOnEditorActionListener { _, actionId, _ -> editText.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) { if (actionId == EditorInfo.IME_ACTION_DONE) {
delegate.onRowValueChanged(getValue(), row) delegate.onRowValueChanged(getValue(), row)
dismiss() dismiss()

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -12,8 +12,9 @@ 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
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.StaticDataRowEditableDescriptor
class BottomSheetStaticListFragment : BottomSheetFragment(), StaticRowRepresentableDataSource, class InputStaticListFragment(row: RowRepresentable) : InputFragment(row), StaticRowRepresentableDataSource,
RowRepresentableDelegate { RowRepresentableDelegate {
private var staticRows: List<RowRepresentable> = emptyList() private var staticRows: List<RowRepresentable> = emptyList()
@ -44,14 +45,14 @@ class BottomSheetStaticListFragment : BottomSheetFragment(), StaticRowRepresenta
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (bottomSheetData.size != 1) { if (bottomSheetData.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
if (bottomSheetData.first().staticData == null) { val dataList = bottomSheetData[0]
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") if (dataList !is StaticDataRowEditableDescriptor) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
} if (dataList.staticData == null) { throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency") }
this.staticRows = bottomSheetData.first().staticData as List<RowRepresentable> this.staticRows= dataList.staticData as List<RowRepresentable>
} }
/** /**

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
@ -10,12 +10,13 @@ import kotlinx.android.synthetic.main.bottom_sheet_sum.*
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.ui.view.RowRepresentable
import net.pokeranalytics.android.util.extensions.round import net.pokeranalytics.android.util.extensions.round
import net.pokeranalytics.android.util.extensions.toCurrency import net.pokeranalytics.android.util.extensions.toCurrency
import java.text.NumberFormat import java.text.NumberFormat
class BottomSheetSumFragment : BottomSheetFragment() { class InputSumFragment(row: RowRepresentable) : InputFragment(row) {
private var value = 0.0 private var value = 0.0
private var currentDefaultValue = 0.0 private var currentDefaultValue = 0.0
@ -28,7 +29,7 @@ class BottomSheetSumFragment : BottomSheetFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
editText1.requestFocus() editText.requestFocus()
} }
override fun getValue(): Any? { override fun getValue(): Any? {
@ -49,9 +50,9 @@ class BottomSheetSumFragment : BottomSheetFragment() {
* Init UI * Init UI
*/ */
private fun initUI() { private fun initUI() {
val data = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val data = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (data.size != 5) { if (data.size != 5) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_sum, view?.bottomSheetContainer, true) LayoutInflater.from(requireContext()).inflate(R.layout.bottom_sheet_sum, view?.bottomSheetContainer, true)
@ -98,11 +99,11 @@ class BottomSheetSumFragment : BottomSheetFragment() {
} }
// First edit text // First edit text
data[3].hint?.let { editText1.hint = getString(it) } data[3].hint?.let { editText.hint = getString(it) }
editText1.inputType = data[3].inputType ?: InputType.TYPE_CLASS_TEXT editText.inputType = data[3].inputType ?: InputType.TYPE_CLASS_TEXT
editText1.addTextChangedListener { editText.addTextChangedListener {
val valueToAdd = try { val valueToAdd = try {
editText1.text.toString().toDouble() editText.text.toString().toDouble()
} catch (e: Exception) { } catch (e: Exception) {
0.0 0.0
} }

@ -1,4 +1,4 @@
package net.pokeranalytics.android.ui.fragment.components.bottomsheet package net.pokeranalytics.android.ui.fragment.components.input
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -11,11 +11,11 @@ import net.pokeranalytics.android.model.TableSize
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
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.view.GridSpacingItemDecoration import net.pokeranalytics.android.ui.view.GridSpacingItemDecoration
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.extensions.px
class BottomSheetTableSizeGridFragment : BottomSheetFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { class InputTableSizeGridFragment(row: RowRepresentable) : InputFragment(row), StaticRowRepresentableDataSource, RowRepresentableDelegate {
private lateinit var dataAdapter: RowRepresentableAdapter private lateinit var dataAdapter: RowRepresentableAdapter
private var defaultSize: Int? = null private var defaultSize: Int? = null
@ -39,9 +39,9 @@ class BottomSheetTableSizeGridFragment : BottomSheetFragment(), StaticRowReprese
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor not found") val bottomSheetData = getData()?:throw RowRepresentableEditDescriptorException("RowEditableDescriptor not found")
if (bottomSheetData.size != 1) { if (bottomSheetData.size != 1) {
throw RowRepresentableEditDescriptorException("RowRepresentableEditDescriptor inconsistency") throw RowRepresentableEditDescriptorException("RowEditableDescriptor inconsistency")
} }
defaultSize = bottomSheetData.first().defaultValue as Int? defaultSize = bottomSheetData.first().defaultValue as Int?
} }

@ -9,15 +9,14 @@ import net.pokeranalytics.android.api.CurrencyConverterApi
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.retrofit.CurrencyConverterValue import net.pokeranalytics.android.model.retrofit.CurrencyConverterValue
import net.pokeranalytics.android.ui.activity.CurrenciesActivity import net.pokeranalytics.android.ui.activity.CurrenciesActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
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.fragment.CurrenciesFragment import net.pokeranalytics.android.ui.fragment.CurrenciesFragment
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.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow import net.pokeranalytics.android.ui.view.rowrepresentable.BankrollRow
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.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.toCurrency import net.pokeranalytics.android.util.extensions.toCurrency
@ -33,10 +32,6 @@ import java.util.*
*/ */
class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataSource { class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataSource {
companion object {
const val REQUEST_CODE_CURRENCY: Int = 100
}
// Return the item as a Bankroll object // Return the item as a Bankroll object
private val bankroll: Bankroll private val bankroll: Bankroll
get() { get() {
@ -67,7 +62,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_CURRENCY && resultCode == RESULT_OK) { if (requestCode == RequestCode.CURRENCY.value && resultCode == RESULT_OK) {
data?.let { data?.let {
val currencyCode = it.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE) val currencyCode = it.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE)
onRowValueChanged(currencyCode, BankrollRow.CURRENCY) onRowValueChanged(currencyCode, BankrollRow.CURRENCY)
@ -88,7 +83,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> if (bankroll.name.isNotEmpty()) bankroll.name else NULL_TEXT BankrollRow.NAME -> if (bankroll.name.isNotEmpty()) bankroll.name else NULL_TEXT
BankrollRow.CURRENCY -> { BankrollRow.CURRENCY -> {
bankroll.currency?.code?.let { code -> bankroll.currency?.code?.let { code ->
Currency.getInstance(code).currencyCode Currency.getInstance(code).currencyCode
@ -119,24 +114,10 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) {
SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.bankroll.name))
BankrollRow.INITIAL_VALUE -> {
row.editingDescriptors(mapOf("defaultValue" to this.bankroll.initialValue))
}
BankrollRow.RATE -> {
val rate = this.bankroll.currency?.rate
row.editingDescriptors(mapOf("defaultValue" to rate))
}
else -> null
}
}
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) { when (row) {
BankrollRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@BankrollDataFragment, BankrollRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@BankrollDataFragment,
REQUEST_CODE_CURRENCY RequestCode.CURRENCY.value
) )
BankrollRow.REFRESH_RATE -> refreshRate() BankrollRow.REFRESH_RATE -> refreshRate()
else -> super.onRowSelected(position, row, fromAction) else -> super.onRowSelected(position, row, fromAction)
@ -146,9 +127,11 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
super.onRowValueChanged(value, row) super.onRowValueChanged(value, row)
// Clear the value when the currency has been updated when (row) {
if (row == BankrollRow.CURRENCY) { BankrollRow.CURRENCY -> { // Clear the value when the currency has been updated
this.lastRefreshRateCall = 0 this.lastRefreshRateCall = 0
this.rowRepresentableAdapter.notifyDataSetChanged()
}
} }
updateAdapterUI() updateAdapterUI()
@ -175,7 +158,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
private fun refreshRows() { private fun refreshRows() {
rows.clear() rows.clear()
rows.add(SimpleRow.NAME) rows.add(BankrollRow.NAME)
rows.add(BankrollRow.LIVE) rows.add(BankrollRow.LIVE)
rows.add(BankrollRow.INITIAL_VALUE) rows.add(BankrollRow.INITIAL_VALUE)
rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.currency)) rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.currency))
@ -213,7 +196,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
} }
lastRefreshRateCall = System.currentTimeMillis() lastRefreshRateCall = System.currentTimeMillis()
val currenciesConverterValue = "${defaultCurrency.currencyCode}_${bankroll.currency?.code}" val currenciesConverterValue = "${bankroll.currency?.code}_${defaultCurrency.currencyCode}"
val call = CurrencyConverterApi.getApi(requireContext())?.convert(currenciesConverterValue) val call = CurrencyConverterApi.getApi(requireContext())?.convert(currenciesConverterValue)
call?.enqueue(object : retrofit2.Callback<Map<String, CurrencyConverterValue>> { call?.enqueue(object : retrofit2.Callback<Map<String, CurrencyConverterValue>> {

@ -17,11 +17,8 @@ import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension 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.extensions.showAlertDialog
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.rowrepresentable.CustomFieldRow import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import java.util.* import java.util.*
@ -122,7 +119,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SimpleRow.NAME -> if (customField.name.isNotEmpty()) customField.name else NULL_TEXT CustomFieldRow.NAME -> if (customField.name.isNotEmpty()) customField.name else NULL_TEXT
else -> super.stringForRow(row) else -> super.stringForRow(row)
} }
} }
@ -142,20 +139,19 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
} }
} }
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? { /*
override fun editDescriptors(row: RowRepresentable): ArrayList<RowEditableDescriptor>? {
return when (row) { return when (row) {
SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.customField.name)) SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.customField.name))
is CustomFieldEntry -> row.editingDescriptors(mapOf("defaultValue" to row.value)) is CustomFieldEntry -> row.editingDescriptors(mapOf("defaultValue" to row.value))
else -> null else -> null
} }
} }
*/
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
when (row) { when (row) {
is CustomFieldEntry -> { is CustomFieldEntry -> row.startEditing(null, this)
val data = customField.editDescriptors(row)
BottomSheetFragment.create(fragmentManager, row, this, data, isClearable = false, isDeletable = true)
}
else -> super.onRowSelected(position, row, fromAction) else -> super.onRowSelected(position, row, fromAction)
} }
} }
@ -187,10 +183,10 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
customField.deleteEntry(row) customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged() rowRepresentableAdapter.notifyDataSetChanged()
}) })
return } else {
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
} }
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
} }
} }
} }
@ -271,4 +267,11 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
} }
} }
override fun onDataSaved() {
super.onDataSaved()
this.customField.cleanupEntries()
}
} }

@ -128,6 +128,8 @@ open class DataManagerFragment : RealmFragment() {
*/ */
protected open fun deleteData() { protected open fun deleteData() {
this.willDeleteData()
val realm = this.getRealm() val realm = this.getRealm()
if (this.item.isValidForDelete(realm)) { if (this.item.isValidForDelete(realm)) {
@ -145,6 +147,8 @@ open class DataManagerFragment : RealmFragment() {
} }
} }
open fun willDeleteData() { }
/** /**
* Finish the activity with a result * Finish the activity with a result
*/ */

@ -14,7 +14,6 @@ import net.pokeranalytics.android.model.interfaces.Editable
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
@ -62,7 +61,7 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
BottomSheetFragment.create(fragmentManager, row, this, getDataSource().editDescriptors(row)) row.startEditing(this.item, this)
} }
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save