Replace synchronized transaction by async

realmasync
Laurent 2 years ago
parent b5769173b7
commit 341cca8735
  1. 4
      app/src/androidTest/java/net/pokeranalytics/android/unitTests/StatsInstrumentedUnitTest.kt
  2. 21
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  3. 93
      app/src/main/java/net/pokeranalytics/android/calculus/ReportWhistleBlower.kt
  4. 2
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt
  5. 6
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReportManager.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/model/Criteria.kt
  7. 19
      app/src/main/java/net/pokeranalytics/android/model/LiveData.kt
  8. 7
      app/src/main/java/net/pokeranalytics/android/model/filter/Filterable.kt
  9. 58
      app/src/main/java/net/pokeranalytics/android/model/migrations/Patcher.kt
  10. 42
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  11. 10
      app/src/main/java/net/pokeranalytics/android/model/realm/ComputableResult.kt
  12. 6
      app/src/main/java/net/pokeranalytics/android/model/realm/Currency.kt
  13. 38
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  14. 15
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  15. 16
      app/src/main/java/net/pokeranalytics/android/model/realm/Filter.kt
  16. 9
      app/src/main/java/net/pokeranalytics/android/model/realm/Performance.kt
  17. 4
      app/src/main/java/net/pokeranalytics/android/model/realm/ReportSetup.kt
  18. 24
      app/src/main/java/net/pokeranalytics/android/model/realm/Result.kt
  19. 426
      app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt
  20. 49
      app/src/main/java/net/pokeranalytics/android/model/realm/UserConfig.kt
  21. 8
      app/src/main/java/net/pokeranalytics/android/model/realm/handhistory/HandHistory.kt
  22. 13
      app/src/main/java/net/pokeranalytics/android/model/utils/FavoriteSessionFinder.kt
  23. 273
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionManager.kt
  24. 209
      app/src/main/java/net/pokeranalytics/android/model/utils/SessionSetManager.kt
  25. 13
      app/src/main/java/net/pokeranalytics/android/ui/activity/HomeActivity.kt
  26. 39
      app/src/main/java/net/pokeranalytics/android/ui/activity/components/MediaActivity.kt
  27. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/ImportFragment.kt
  28. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  29. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt
  30. 15
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  31. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/FilterableFragment.kt
  32. 10
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/bottomsheet/BottomSheetListGameFragment.kt
  33. 49
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
  34. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComposableTableReportFragment.kt
  35. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  36. 6
      app/src/main/java/net/pokeranalytics/android/ui/modules/bankroll/BankrollFragment.kt
  37. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt
  38. 5
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt
  39. 5
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/GridCalendarViewModel.kt
  40. 27
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt
  41. 20
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/EditableDataFragment.kt
  42. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/PlayerDataFragment.kt
  43. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/PlayerDataViewModel.kt
  44. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/TransactionDataFragment.kt
  45. 12
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/FeedFragment.kt
  46. 5
      app/src/main/java/net/pokeranalytics/android/ui/modules/feed/NewDataMenuActivity.kt
  47. 4
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt
  48. 13
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterHandler.kt
  49. 23
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt
  50. 22
      app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListFragment.kt
  51. 11
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt
  52. 96
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/model/EditorViewModel.kt
  53. 22
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayExportService.kt
  54. 84
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt
  55. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionViewModel.kt
  56. 9
      app/src/main/java/net/pokeranalytics/android/ui/modules/settings/DealtHandsPerHourFragment.kt
  57. 12
      app/src/main/java/net/pokeranalytics/android/ui/modules/settings/TransactionFilterFragment.kt
  58. 3
      app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt
  59. 6
      app/src/main/java/net/pokeranalytics/android/ui/view/SessionRowView.kt
  60. 28
      app/src/main/java/net/pokeranalytics/android/ui/viewmodel/BottomSheetViewModel.kt
  61. 11
      app/src/main/java/net/pokeranalytics/android/util/FakeDataManager.kt
  62. 5
      app/src/main/java/net/pokeranalytics/android/util/csv/CSVDescriptor.kt
  63. 21
      app/src/main/java/net/pokeranalytics/android/util/csv/PACSVDescriptor.kt
  64. 10
      app/src/main/java/net/pokeranalytics/android/util/csv/SessionCSVDescriptor.kt
  65. 37
      app/src/main/java/net/pokeranalytics/android/util/extensions/RealmExtensions.kt

@ -186,13 +186,13 @@ class StatsInstrumentedUnitTest : SessionInstrumentedUnitTest() {
Assert.fail("No std100 stat")
}
results.computedStat(Stat.MAXIMUM_NETRESULT)?.let {
results.computedStat(Stat.MAXIMUM_NET_RESULT)?.let {
assertEquals(300.0, it.value, delta)
} ?: run {
Assert.fail("No MAXIMUM_NETRESULT")
}
results.computedStat(Stat.MINIMUM_NETRESULT)?.let {
results.computedStat(Stat.MINIMUM_NET_RESULT)?.let {
assertEquals(-100.0, it.value, delta)
} ?: run {
Assert.fail("No MINIMUM_NETRESULT")

@ -14,7 +14,9 @@ import net.pokeranalytics.android.calculus.ReportWhistleBlower
import net.pokeranalytics.android.model.migrations.Patcher
import net.pokeranalytics.android.model.migrations.PokerAnalyticsMigration
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.UserConfigObserver
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.model.utils.SessionManager
import net.pokeranalytics.android.util.*
import net.pokeranalytics.android.util.billing.AppGuard
import timber.log.Timber
@ -44,6 +46,11 @@ class PokerAnalyticsApplication : Application() {
UserDefaults.init(this)
if (BuildConfig.DEBUG) {
// Logs
Timber.plant(PokerAnalyticsLogs())
}
// AppGuard / Billing services
AppGuard.load(this.applicationContext)
@ -51,8 +58,8 @@ class PokerAnalyticsApplication : Application() {
Realm.init(this)
val realmConfiguration = RealmConfiguration.Builder()
.name(Realm.DEFAULT_REALM_NAME)
.schemaVersion(14)
.allowWritesOnUiThread(true)
.schemaVersion(15)
// .allowWritesOnUiThread(true)
.migration(PokerAnalyticsMigration())
.initialData(Seed(this))
.build()
@ -63,23 +70,19 @@ class PokerAnalyticsApplication : Application() {
CrashLogging.log("App onCreate. Locales = $locales")
}
if (BuildConfig.DEBUG) {
// Logs
Timber.plant(PokerAnalyticsLogs())
}
Timber.d("SDK version = ${Build.VERSION.SDK_INT}")
if (BuildConfig.DEBUG) {
Timber.d("UserPreferences.defaultCurrency: ${UserDefaults.currency.symbol}")
Timber.d("Realm path = ${Realm.getDefaultInstance().path}")
// this.createFakeSessions()
}
// Patch
Patcher.patchAll(this)
// Report
// Processors
SessionManager.create()
UserConfigObserver.create()
this.reportWhistleBlower = ReportWhistleBlower(this.applicationContext)
// Backups

@ -10,9 +10,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.optimalduration.CashGameOptimalDurationCalculator
import net.pokeranalytics.android.model.LiveOnline
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.Performance
import net.pokeranalytics.android.model.realm.PerformanceKey
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.formattedHourlyDuration
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
@ -24,7 +29,7 @@ interface NewPerformanceListener {
class ReportWhistleBlower(var context: Context) {
private var sessions: RealmResults<Session>? = null
private var results: RealmResults<Result>? = null
// private var results: RealmResults<Result>? = null
private var currentTask: ReportTask? = null
@ -45,10 +50,10 @@ class ReportWhistleBlower(var context: Context) {
requestReportLaunch()
}
this.results = realm.where(Result::class.java).findAll()
this.results?.addChangeListener { _ ->
requestReportLaunch()
}
// this.results = realm.where(Result::class.java).findAll()
// this.results?.addChangeListener { _ ->
// requestReportLaunch()
// }
realm.close()
}
@ -71,8 +76,8 @@ class ReportWhistleBlower(var context: Context) {
this.timer?.cancel()
val launchStart = 100L
val timer = object: CountDownTimer(launchStart, launchStart) {
override fun onTick(p0: Long) { }
val timer = object : CountDownTimer(launchStart, launchStart) {
override fun onTick(p0: Long) {}
override fun onFinish() {
launchReportTask()
@ -212,7 +217,10 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
customField?.let {
query = query.equalTo("customFieldId", it.id)
}
val currentPerf = query.findFirst()
var currentPerf = query.findFirst()
if (currentPerf != null) {
currentPerf = realm.copyFromRealm(currentPerf)
}
// Store if necessary, delete if necessary
val bestComputedResults = result.max(stat)
@ -226,7 +234,11 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
var storePerf = true
currentPerf?.let {
currentPerf.name?.let { name ->
if (computedResults.group.query.getName(this.context, nameSeparator) == name) {
if (computedResults.group.query.getName(
this.context,
nameSeparator
) == name
) {
storePerf = false
}
}
@ -237,12 +249,13 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
if (storePerf) {
realm.executeTransaction {
currentPerf.name = performanceName
currentPerf.objectId = performanceQuery.objectId
currentPerf.customFieldId = customField?.id
currentPerf.name = performanceName
currentPerf.objectId = performanceQuery.objectId
currentPerf.customFieldId = customField?.id
realm.writeAsync { asyncRealm ->
asyncRealm.copyToRealm(currentPerf)
this.whistleBlower.notify(currentPerf)
}
this.whistleBlower.notify(currentPerf)
}
}
@ -256,16 +269,20 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
customField?.id,
null
)
realm.executeTransaction { it.copyToRealm(performance) }
this.whistleBlower.notify(performance)
realm.writeAsync { asyncRealm ->
asyncRealm.copyToRealm(performance)
this.whistleBlower.notify(performance)
}
}
} ?: run { // if there is no max but a now irrelevant Performance, we delete it
Timber.d("NO best computed value, current perf = $currentPerf ")
currentPerf?.let { perf ->
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
Timber.d("Delete perf: stat = ${perf.stat}, report = ${perf.reportId}")
perf.deleteFromRealm()
asyncRealm.findById<Performance>(perf.id)?.deleteFromRealm()
// perf.deleteFromRealm()
}
}
}
@ -274,9 +291,17 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
private fun analyseOptimalDuration(realm: Realm, staticReport: StaticReport, key: PerformanceKey, duration: Double?) {
private fun analyseOptimalDuration(
realm: Realm,
staticReport: StaticReport,
key: PerformanceKey,
duration: Double?
) {
val performance = performancesQuery(realm, staticReport, key).findFirst()
var performance = performancesQuery(realm, staticReport, key).findFirst()
if (performance != null) {
performance = realm.copyFromRealm(performance)
}
duration?.let {
var storePerf = true
@ -288,30 +313,38 @@ class ReportTask(private var whistleBlower: ReportWhistleBlower, var context: Co
}
if (storePerf) {
realm.executeTransaction {
perf.name = formattedDuration
perf.value = duration
perf.name = formattedDuration
perf.value = duration
realm.writeAsync { asyncRealm ->
asyncRealm.copyToRealm(perf)
}
}
}
if (storePerf) {
val perf = Performance(staticReport, key, name = formattedDuration, value = duration)
realm.executeTransaction { it.copyToRealm(perf) }
this.whistleBlower.notify(perf)
val perf =
Performance(staticReport, key, name = formattedDuration, value = duration)
realm.writeAsync { asyncRealm ->
asyncRealm.copyToRealm(perf)
this.whistleBlower.notify(perf)
}
}
} ?: run { // no duration
performance?.let { perf ->
realm.executeTransaction {
perf.deleteFromRealm() // delete if the perf exists
realm.writeAsync { asyncRealm ->
asyncRealm.findById<Performance>(perf.id)?.deleteFromRealm()
}
}
}
}
private fun performancesQuery(realm: Realm, staticReport: StaticReport, key: PerformanceKey): RealmQuery<Performance> {
private fun performancesQuery(
realm: Realm,
staticReport: StaticReport,
key: PerformanceKey
): RealmQuery<Performance> {
return realm.where(Performance::class.java)
.equalTo("reportId", staticReport.uniqueIdentifier)
.equalTo("key", key.value)

@ -89,7 +89,7 @@ class BankrollCalculator {
this.computeRiskOfRuin(report, result)
} else {
val results = Filter.queryOn<Result>(realm, baseQuery)
val results = Filter.queryOn<Session>(realm, baseQuery)
report.netResult = results.sum("net").toDouble()
}

@ -2,8 +2,8 @@ package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm
import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.model.realm.Bankroll
@ -68,10 +68,10 @@ object BankrollReportManager {
}
// otherwise compute it
GlobalScope.launch(coroutineContext) {
CoroutineScope(context = coroutineContext).launch {
var report: BankrollReport? = null
val coroutine = GlobalScope.async {
val coroutine = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>>>> start computing bankroll...")

@ -87,7 +87,7 @@ sealed class Criteria(override var uniqueIdentifier: Int) : IntIdentifiable, Row
if (this is ValueCustomFields) {
val realm = Realm.getDefaultInstance()
val distincts = realm.where<CustomFieldEntry>().equalTo("customFields.id", this.customFieldId).findAll().sort("numericValue", Sort.ASCENDING)
val distincts = realm.where<CustomFieldEntry>().equalTo("customField.id", this.customFieldId).findAll().sort("numericValue", Sort.ASCENDING)
realm.close()
val objects = mutableListOf<QueryCondition.CustomFieldNumberQuery>()

@ -51,12 +51,17 @@ enum class LiveData : Localizable {
}
fun updateOrCreate(realm: Realm, primaryKey: String?): Deletable {
val proxyItem: Deletable? = this.getData(realm, primaryKey)
proxyItem?.let {
return realm.copyFromRealm(it)
} ?: run {
return this.newEntity()
}
val data = this.getData(realm, primaryKey)
data?.let { return data }
return this.newEntity()
// val proxyItem: Deletable? = this.getData(realm, primaryKey)
// proxyItem?.let {
// return realm.copyFromRealm(it)
// } ?: run {
// return this.newEntity()
// }
}
private fun newEntity(): Deletable {
@ -68,7 +73,7 @@ enum class LiveData : Localizable {
primaryKey?.let {
val t = realm.findById(this.relatedEntity, it)
t?.let {
proxyItem = t
proxyItem = realm.copyFromRealm(t) // make an unmanaged object
}
}
return proxyItem

@ -3,7 +3,10 @@ package net.pokeranalytics.android.model.filter
import io.realm.RealmModel
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.SessionSet
import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.util.CrashLogging
/**
@ -63,7 +66,7 @@ class FilterHelper {
ComputableResult::class.java -> ComputableResult.fieldNameForQueryType(queryCondition)
SessionSet::class.java -> SessionSet.fieldNameForQueryType(queryCondition)
Transaction::class.java -> Transaction.fieldNameForQueryType(queryCondition)
Result::class.java -> Result.fieldNameForQueryType(queryCondition)
// Result::class.java -> Result.fieldNameForQueryType(queryCondition)
else -> {
CrashLogging.logException(PAIllegalStateException("Filterable type fields are not defined for condition ${queryCondition::class}, class ${T::class}"))
null

@ -9,9 +9,10 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.realm.*
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.utils.Seed
import net.pokeranalytics.android.model.utils.SessionSetManager
import net.pokeranalytics.android.model.utils.SessionManager
import net.pokeranalytics.android.util.BLIND_SEPARATOR
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.writeAsync
import java.text.NumberFormat
class Patcher {
@ -70,8 +71,8 @@ class Patcher {
val transactionTypes = TransactionType.Value.values()
realm.executeTransaction {
Seed.createDefaultTransactionTypes(transactionTypes, context, realm)
realm.writeAsync { asyncRealm ->
Seed.createDefaultTransactionTypes(transactionTypes, context, asyncRealm)
}
realm.close()
@ -80,19 +81,17 @@ class Patcher {
private fun patchBreaks() {
val realm = Realm.getDefaultInstance()
val sets = realm.where(SessionSet::class.java).findAll()
val sessions = Filter.queryOn<Session>(realm, Query(QueryCondition.IsCash))
val results = realm.where(Result::class.java).findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val sets = asyncRealm.where(SessionSet::class.java).findAll()
val sessions = Filter.queryOn<Session>(asyncRealm, Query(QueryCondition.IsCash))
sets.forEach {
it.computeStats()
}
sessions.forEach {
it.generateStakes()
it.defineHighestBet()
}
results.forEach {
it.computeNumberOfRebuy()
}
}
@ -103,8 +102,8 @@ class Patcher {
private fun patchDefaultTransactionTypes(context: Context) {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val tts = realm.where(TransactionType::class.java).findAll()
realm.writeAsync { asyncRealm ->
val tts = asyncRealm.where(TransactionType::class.java).findAll()
tts.forEach { tt ->
tt.kind?.let { kind ->
val value = TransactionType.Value.values()[kind]
@ -117,8 +116,8 @@ class Patcher {
private fun patchStakes() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val sessions = realm.where(Session::class.java).findAll()
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where(Session::class.java).findAll()
sessions.forEach { session ->
val blinds = arrayListOf(session.cgOldSmallBlind, session.cgOldBigBlind).filterNotNull()
val blindsFormatted = blinds.map { NumberFormat.getInstance().format(it) }
@ -128,7 +127,7 @@ class Patcher {
}
}
val handHistories = realm.where(HandHistory::class.java).findAll()
val handHistories = asyncRealm.where(HandHistory::class.java).findAll()
handHistories.forEach { hh ->
val blinds = arrayListOf(hh.oldSmallBlind, hh.oldBigBlind).filterNotNull()
val blindsFormatted = blinds.map { NumberFormat.getInstance().format(it) }
@ -143,8 +142,8 @@ class Patcher {
private fun patchNegativeLimits() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val sessions = realm.where(Session::class.java).lessThan("limit", 0).findAll()
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where(Session::class.java).lessThan("limit", 0).findAll()
sessions.forEach { session ->
session.limit = null
}
@ -154,10 +153,10 @@ class Patcher {
private fun cleanBlindsFilters() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val blindFilterConditions = realm.where(FilterCondition::class.java).equalTo("filterName", "AnyBlind").findAll()
realm.writeAsync { asyncRealm ->
val blindFilterConditions = asyncRealm.where(FilterCondition::class.java).equalTo("filterName", "AnyBlind").findAll()
val filterIds = blindFilterConditions.mapNotNull { it.filters?.firstOrNull() }.map { it.id }
val filters = realm.where(Filter::class.java).`in`("id", filterIds.toTypedArray()).findAll()
val filters = asyncRealm.where(Filter::class.java).`in`("id", filterIds.toTypedArray()).findAll()
filters.deleteAllFromRealm()
}
realm.close()
@ -170,11 +169,12 @@ class Patcher {
private fun patchSessionSet() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(SessionSet::class.java).findAll().deleteAllFromRealm()
val sessions = realm.where(Session::class.java).isNotNull("startDate").isNotNull("endDate").findAll()
realm.writeAsync { asyncRealm ->
asyncRealm.where(SessionSet::class.java).findAll().deleteAllFromRealm()
val sessions = asyncRealm.where(Session::class.java)
.isNotNull("startDate").isNotNull("endDate").findAll()
sessions.forEach { session ->
SessionSetManager.updateTimeline(session)
SessionManager.updateTimeline(session)
}
}
realm.close()
@ -187,8 +187,8 @@ class Patcher {
*/
private fun patchComputableResults() {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
val crs = realm.where(ComputableResult::class.java).findAll()
realm.writeAsync { asyncRealm ->
val crs = asyncRealm.where(ComputableResult::class.java).findAll()
crs.forEach { cr ->
cr.session?.let { cr.updateWith(it) }
}
@ -210,8 +210,8 @@ class Patcher {
private fun patchZeroTable() {
val realm = Realm.getDefaultInstance()
val zero = 0
val sessions = realm.where<Session>().equalTo("numberOfTables", zero).findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val sessions = asyncRealm.where<Session>().equalTo("numberOfTables", zero).findAll()
sessions.forEach { s ->
s.numberOfTables = 1
}
@ -221,8 +221,8 @@ class Patcher {
private fun patchRatedAmounts() {
val realm = Realm.getDefaultInstance()
val transactions = realm.where<Transaction>().findAll()
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
val transactions = asyncRealm.where<Transaction>().findAll()
transactions.forEach { t ->
t.computeRatedAmount()
}

@ -1,6 +1,7 @@
package net.pokeranalytics.android.model.migrations
import io.realm.DynamicRealm
import io.realm.DynamicRealmObject
import io.realm.RealmMigration
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import timber.log.Timber
@ -335,6 +336,47 @@ class PokerAnalyticsMigration : RealmMigration {
currentVersion++
}
// Migrate to version 15
if (currentVersion == 14) {
schema.get("Transaction")?.let { ts ->
schema.get("Session")?.let { ss ->
ss.addField("buyin", Double::class.java).setNullable("buyin", true)
.addField("cashout", Double::class.java).setNullable("cashout", true)
.addField("netResult", Double::class.java).setNullable("netResult", true)
.addField("net", Double::class.java)
.addField("tips", Double::class.java).setNullable("tips", true)
.addRealmListField("transactions", ts)
.addField("tournamentFinalPosition", Int::class.java).setNullable("tournamentFinalPosition", true)
.addField("numberOfRebuy", Double::class.java).setNullable("numberOfRebuy", true)
.transform { obj ->
val result = obj.get<DynamicRealmObject>("result")
obj.set("buyin", result.get("buyin"))
obj.set("cashout", result.get("cashout"))
obj.set("netResult", result.get("netResult"))
obj.set("net", result.get("net"))
obj.set("tips", result.get("tips"))
obj.set("tournamentFinalPosition", result.get("tournamentFinalPosition"))
obj.set("numberOfRebuy", result.get("numberOfRebuy"))
obj.set("transactions", result.get("transactions"))
}
}
}
schema.get("CustomFieldEntry")?.let { cfes ->
schema.get("CustomField")?.let { cfs ->
cfes.addRealmObjectField("customField", cfs)
cfs.transform { customField ->
val entries = customField.getList("entries")
for (entry in entries) {
entry.setObject("customField", customField)
}
}
}
}
currentVersion++
}
}
override fun equals(other: Any?): Boolean {

@ -28,12 +28,10 @@ open class ComputableResult : RealmObject(), Filterable {
val rate = session.bankroll?.currency?.rate ?: 1.0
session.result?.let { result ->
this.ratedNet = result.net * rate
this.isPositive = result.isPositive
this.ratedBuyin = (result.buyin ?: 0.0) * rate
this.ratedTips = (result.tips ?: 0.0) * rate
}
this.ratedNet = session.net * rate
this.isPositive = session.isPositive
this.ratedBuyin = (session.buyin ?: 0.0) * rate
this.ratedTips = (session.tips ?: 0.0) * rate
this.bbNet = session.bbNet
this.hasBigBlind = if (session.cgBiggestBet != null) 1 else 0

@ -5,6 +5,7 @@ import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.util.UserDefaults
import timber.log.Timber
import java.util.*
open class Currency : RealmObject() {
@ -42,15 +43,16 @@ open class Currency : RealmObject() {
fun refreshRelatedRatedValues() {
Timber.d("refreshRelatedRatedValues of $code")
val rate = this.rate ?: DEFAULT_RATE
val query = this.realm.where(ComputableResult::class.java)
query.`in`("session.bankroll.currency.id", arrayOf(this.id))
val cResults = query.findAll()
cResults.forEach { computable ->
computable.session?.result?.net?.let {
computable.session?.net?.let {
computable.ratedNet = it * rate
}
computable.session?.result?.buyin?.let {
computable.session?.buyin?.let {
computable.ratedBuyin = it * rate
}

@ -24,9 +24,8 @@ import net.pokeranalytics.android.ui.view.rows.CustomFieldPropertiesRow
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.SimpleRow
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import timber.log.Timber
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
import kotlin.collections.ArrayList
open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameManageable, StaticRowRepresentableDataSource {
@ -167,10 +166,9 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
return R.string.cf_entry_delete_popup_message
}
override fun deleteDependencies(realm: Realm) {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customFields.id", id).findAll()
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
@ -230,6 +228,7 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
*/
fun addEntry(): CustomFieldEntry {
val entry = CustomFieldEntry()
entry.customField = this
this.entries.add(entry)
sortEntries()
updateRowRepresentation()
@ -250,9 +249,11 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
fun cleanupEntries() { // called when saving the custom field
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
this.entriesToDelete.forEach { // entries are out of realm
realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
val ids = this.entriesToDelete.map { it.id }
realm.writeAsync { asyncRealm ->
ids.forEach { id -> // entries are out of realm
asyncRealm.where<CustomFieldEntry>().equalTo("id", id).findFirst()?.deleteFromRealm()
}
}
realm.close()
@ -261,37 +262,16 @@ open class CustomField : RealmObject(), RowRepresentable, RowUpdatable, NameMana
fun getOrCreateEntry(realm: Realm, value: String): CustomFieldEntry {
this.entries.find { it.value == value }?.let {
Timber.d("L>> get")
return it
} ?: run {
Timber.d("L>> create")
val entry = realm.copyToRealm(CustomFieldEntry())
entry.customField = this
entry.value = value
this.entries.add(entry)
return entry
}
}
/**
* Clean the entries if the type is not a list & remove the deleted entries from realm
*/
// fun cleanEntries(realm: Realm) {
// realm.executeTransaction {
//
// if (!isListType) {
// entriesToDelete.addAll(entries)
// entries.clear()
// }
//
// // @TODO
// entriesToDelete.forEach {
// Timber.d("Delete entry: V=${it.value} N=${it.numericValue} / ID=${it.id}")
// realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
// }
// entriesToDelete.clear()
// }
// }
/**
* Returns a comparison criteria based on this custom field
*/

@ -4,9 +4,7 @@ import android.content.Context
import android.text.InputType
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.Ignore
import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
@ -43,13 +41,14 @@ open class CustomFieldEntry : RealmObject(), NameManageable, RowRepresentable, R
/**
* The inverse relationship with CustomField
*/
@LinkingObjects("entries")
val customFields: RealmResults<CustomField>? = null
// @LinkingObjects("entries")
// val customFields: RealmResults<CustomField>? = null
val customField: CustomField?
get() {
return this.customFields?.first()
}
var customField: CustomField? = null
// get() {
// return this.customFields?.first()
// }
/**
* The string value of the entry

@ -9,7 +9,10 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.Query
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.UsageCountable
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.modules.filter.FilterableType
import net.pokeranalytics.android.ui.view.*
@ -56,18 +59,11 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
override val imageClickable: Boolean?
get() = true
@PrimaryKey
override var id = UUID.randomUUID().toString()
// the queryWith name
var name: String = ""
get() {
if (field.isEmpty()) {
return this.query.defaultName
}
return field
}
override var useCount: Int = 0
@ -109,7 +105,7 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
val previousCondition = filterConditions.filter {
it.filterName == newFilterCondition.filterName && it.operator == newFilterCondition.operator
}
filterConditions.removeAll(previousCondition)
filterConditions.removeAll(previousCondition.toSet())
filterConditions.add(newFilterCondition)
}
}
@ -118,7 +114,7 @@ open class Filter : RealmObject(), RowRepresentable, RowUpdatable, Deletable, Us
fun remove(filterCategoryRow: FilterCategoryRow) {
val sections = filterCategoryRow.filterSectionRows.map { it.name }
val savedSections = filterConditions.filter { sections.contains(it.sectionName) }
this.filterConditions.removeAll(savedSections)
this.filterConditions.removeAll(savedSections.toSet())
}
fun countBy(filterCategoryRow: FilterCategoryRow): Int {

@ -2,9 +2,11 @@ package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.LiveOnline
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.rows.StaticReport
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.lookupForNameInAllTablesById
@ -16,10 +18,13 @@ interface PerformanceKey {
val value: Int
}
open class Performance() : RealmObject() {
open class Performance() : RealmObject(), Identifiable {
@PrimaryKey
var id: String = UUID.randomUUID().toString()
override var id: String = UUID.randomUUID().toString()
@Ignore
override val realmObjectClass: Class<out Identifiable> = Performance::class.java
constructor(
report: StaticReport,

@ -6,9 +6,9 @@ import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.calcul.ReportDisplay
import net.pokeranalytics.android.model.Criteria
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
@ -16,6 +16,7 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
import java.util.*
@ -67,6 +68,7 @@ open class ReportSetup : RealmObject(), RowRepresentable, Deletable {
*/
fun options(realm: Realm, reportDisplay: ReportDisplay): Calculator.Options {
Timber.d("stats = ${this.statIds.size}")
val stats = this.statIds.map { Stat.valueByIdentifier(it) }
// Comparison criteria

@ -9,12 +9,13 @@ import io.realm.annotations.RealmClass
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
@RealmClass
open class Result : RealmObject(), Filterable {
companion object {
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition>): String? {
fun fieldNameForQueryType(queryCondition: Class <out QueryCondition>): String? {
Session.fieldNameForQueryType(queryCondition)?.let {
return "sessions.$it"
}
@ -22,6 +23,7 @@ open class Result : RealmObject(), Filterable {
}
}
/**
* The buyin amount
*/
@ -29,6 +31,7 @@ open class Result : RealmObject(), Filterable {
set(value) {
field = value
this.computeNumberOfRebuy()
// SessionManager.sessionNetChanged(this.session)
this.computeNet(true)
}
@ -39,9 +42,6 @@ open class Result : RealmObject(), Filterable {
set(value) {
field = value
this.computeNet(true)
if (value != null) {
this.session?.end()
}
}
/**
@ -49,20 +49,8 @@ open class Result : RealmObject(), Filterable {
*/
var netResult: Double? = null
set(value) {
// this.session?.bankroll?.let { bankroll ->
// if (bankroll.live) {
// throw PAIllegalStateException("Can't set net result on a live bankroll")
// }
// } ?: run {
// throw PAIllegalStateException("Session doesn't have any bankroll")
// }
field = value
this.computeNet(false)
if (value != null) {
this.session?.end()
}
}
/**
@ -105,14 +93,14 @@ open class Result : RealmObject(), Filterable {
val isPositive: Int
get() {
return if (session?.isTournament() == true) {
if (this.cashout ?: -1.0 >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
if ((this.cashout ?: -1.0) >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
} else {
if (this.net >= 0.0) 1 else 0
}
}
// Computes the Net
private fun computeNet(withBuyin: Boolean? = null) {
fun computeNet(withBuyin: Boolean? = null) {
val transactionsSum = transactions.sumOf { it.amount }

@ -13,7 +13,6 @@ import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.StatFormattingException
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.model.Stakes
@ -27,7 +26,7 @@ import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.filter.QueryCondition.*
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.model.utils.SessionSetManager
import net.pokeranalytics.android.model.utils.SessionManager
import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException
import net.pokeranalytics.android.ui.graph.Graph
import net.pokeranalytics.android.ui.view.*
@ -37,6 +36,7 @@ import net.pokeranalytics.android.util.extensions.hourMinute
import net.pokeranalytics.android.util.extensions.shortDateTime
import net.pokeranalytics.android.util.extensions.toCurrency
import net.pokeranalytics.android.util.extensions.toMinutes
import timber.log.Timber
import java.text.DateFormat
import java.text.NumberFormat
import java.text.ParseException
@ -45,7 +45,7 @@ import java.util.Currency
typealias BB = Double
open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Timed,
open class Session : RealmObject(), Savable, RowRepresentable, Timed,
TimeFilterable, Filterable, DatedBankrollGraphEntry, StakesHolder {
enum class Type(val value: String) {
@ -67,24 +67,23 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
companion object {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null, managed: Boolean = true): Session {
fun newInstance(realm: Realm, isTournament: Boolean, bankroll: Bankroll? = null): Session {
val session = Session()
session.result = Result()
if (bankroll != null) {
session.bankroll = bankroll
} else {
session.bankroll = realm.where<Bankroll>().findFirst()
val br: Bankroll? = bankroll ?: realm.where<Bankroll>().findFirst()
br?.let {
session.bankroll = realm.copyFromRealm(br)
}
session.type = if (isTournament) Type.TOURNAMENT.ordinal else Type.CASH_GAME.ordinal
session.type = if (isTournament) Type.TOURNAMENT.ordinal else Type.CASH_GAME.ordinal
session.limit = Limit.NO.ordinal
session.game = realm.where(Game::class.java).equalTo("shortName", "HE").findFirst()
return if (managed) {
realm.copyToRealm(session)
} else {
session
val game = realm.where(Game::class.java).equalTo("shortName", "HE").findFirst()
game?.let {
session.game = realm.copyFromRealm(game)
}
return session
}
fun fieldNameForQueryType(queryCondition: Class < out QueryCondition >): String? {
@ -118,7 +117,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
Duration::class.java -> "netDuration"
CustomFieldListQuery::class.java -> "customFieldEntries.id"
CustomFieldAmountQuery::class.java, CustomFieldNumberQuery::class.java -> "customFieldEntries.numericValue"
CustomFieldQuery::class.java -> "customFieldEntries.customFields.id"
CustomFieldQuery::class.java -> "customFieldEntries.customField.id"
DateNotNull::class.java -> "startDate"
EndDateNotNull::class.java -> "endDate"
BiggestBetNotNull::class.java -> "cgBiggestBet"
@ -148,7 +147,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val sessionType: Type
get() { return Type.values()[this.type] }
// The result of the main user
// Not used anymore
var result: Result? = null
@LinkingObjects("session")
@ -209,7 +208,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.endDate = null
}
this.dateChanged()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -230,7 +229,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.computeNetDuration()
this.dateChanged()
this.defineDefaultTournamentBuyinIfNecessary()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -240,7 +239,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
field = value
this.computeNetDuration()
this.computeStats()
this.addToComputeQueue()
}
/**
@ -268,7 +267,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
field = value
this.generateStakes()
this.computeStats()
this.addToComputeQueue()
// this.updateRowRepresentation()
}
@ -296,7 +295,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
set(value) {
if (value > 0) {
field = value
this.computeStats()
this.addToComputeQueue()
}
}
@ -314,16 +313,13 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
// The small blind value
var cgOldSmallBlind: Double? = null
set(value) {
field = value
}
// The big blind value
var cgOldBigBlind: Double? = null
set(value) {
field = value
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
// var blinds: String? = null
@ -334,8 +330,8 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
field = value
this.generateStakes()
this.defineHighestBet()
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
var cgBlinds: String? = null
@ -343,8 +339,8 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
field = cleanupBlinds(value)
this.generateStakes()
this.defineHighestBet()
this.computeStats()
this.result?.computeNumberOfRebuy()
this.addToComputeQueue()
this.computeNumberOfRebuy()
}
var cgBiggestBet: Double? = null
@ -357,7 +353,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
var tournamentEntryFee: Double? = null
set(value) {
field = value
this.result?.computeNumberOfRebuy()
this.computeNumberOfRebuy()
}
// The total number of players who participated in the tournament
@ -379,9 +375,132 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
var handsCount: Int? = null
set(value) {
field = value
this.computeStats()
this.addToComputeQueue()
}
/**
* The buyin amount
*/
var buyin: Double? = null
set(value) {
field = value
this.computeNumberOfRebuy()
// SessionManager.sessionNetChanged(this.session)
this.computeNet(true)
}
/**
* The cashed out amount
*/
var cashout: Double? = null
set(value) {
field = value
this.computeNet(true)
}
/**
* The net result
*/
var netResult: Double? = null
set(value) {
field = value
this.computeNet(false)
}
/**
* The pre-computed net (readonly)
*/
var net: Double = 0.0
private set
/**
* Tips
*/
var tips: Double? = null
set(value) {
field = value
this.addToComputeQueue()
}
// The transactions associated with the Result, impacting the result
var transactions: RealmList<Transaction> = RealmList()
set(value) {
field = value
this.computeNet()
}
// The tournament final position, if applicable
var tournamentFinalPosition: Int? = null
// Number of rebuys
var numberOfRebuy: Double? = null
//////////////////////////////
// Computes the Net
fun computeNet(withBuyin: Boolean? = null) {
val transactionsSum = transactions.sumOf { it.amount }
// choose the method to compute the net
var useBuyin = withBuyin ?: true
if (withBuyin == null) {
if (netResult != null) {
useBuyin = false
} else if (buyin != null || cashout != null) {
useBuyin = true
} else {
if (this.isCashGame() && !this.isLive) {
useBuyin = false
}
}
}
if (useBuyin) {
val buyin = this.buyin ?: 0.0
val cashOut = this.cashout ?: 0.0
this.net = cashOut - buyin + transactionsSum
} else {
val netResult = this.netResult ?: 0.0
this.net = netResult + transactionsSum
}
// Precompute results
this.addToComputeQueue()
this.sessionSet?.computeStats()
}
// Computes the number of rebuy
fun computeNumberOfRebuy() {
if (this.isCashGame()) {
this.cgBiggestBet?.let { bb ->
if (bb > 0.0) {
this.numberOfRebuy = (this.buyin ?: 0.0) / (bb * 100.0)
} else {
this.numberOfRebuy = null
}
}
} else {
this.tournamentEntryFee?.let { entryFee ->
if (entryFee > 0.0) {
this.numberOfRebuy = (this.buyin ?: 0.0) / entryFee
} else {
this.numberOfRebuy = null
}
}
}
}
val isPositive: Int
get() {
return if (this.isTournament()) {
if ((this.cashout ?: -1.0) >= 0.0) 1 else 0 // if cashout is null we want to count a negative session
} else {
if (this.net >= 0.0) 1 else 0
}
}
//////////////////////////////
fun bankrollHasBeenUpdated() {
this.generateStakes()
}
@ -391,11 +510,15 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Should be called when the start / end date are changed
*/
private fun dateChanged() {
if (this.endDate != null) {
SessionSetManager.updateTimeline(this)
} else if (this.sessionSet != null) {
SessionSetManager.removeFromTimeline(this)
}
SessionManager.sessionDateChanged(this)
// if (this.endDate != null) {
// SessionSetManager.updateTimeline(this)
// } else if (this.sessionSet != null) {
// SessionSetManager.removeFromTimeline(this)
// }
// this.updateRowRepresentation()
}
@ -435,9 +558,9 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val bbNet: BB
get() {
val bb = this.cgBiggestBet
val result = this.result
return if (bb != null && result != null) {
result.net / bb
// val result = this.result
return if (bb != null) {
this.net / bb
} else {
0.0
}
@ -468,9 +591,16 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
@Ignore
override var amount: Double = 0.0
get() {
return this.result?.net ?: 0.0
return this.net
}
/**
* Pre-compute various statIds
*/
private fun addToComputeQueue() {
SessionManager.sessionToCompute(this)
}
/**
* Pre-compute various statIds
*/
@ -501,17 +631,13 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val numberOfHandsPerHour: Double
get() {
val tableSize = this.tableSize ?: 9 // 9 is the default table size if null
val config = UserConfig.getConfiguration(this.realm)
val playerHandsPerHour = if (this.isLive) config.liveDealtHandsPerHour else config.onlineDealtHandsPerHour
return this.numberOfTables * playerHandsPerHour / tableSize.toDouble()
val dealtHandsPerHour = if (this.isLive) UserConfigObserver.liveDealtHandsPerHour else UserConfigObserver.onlineDealtHandsPerHour
return this.numberOfTables * dealtHandsPerHour / tableSize.toDouble()
}
val hourlyRate: Double
get() {
this.result?.let { result ->
return result.net / this.hourlyDuration
}
throw ModelException("Session should have an existing Result relationship")
return this.net / this.hourlyDuration
}
private val bbHourlyRate: BB
@ -529,12 +655,12 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
val hasBuyin: Boolean
get() {
return this.result?.buyin != null
return this.buyin != null
}
val hasNetResult: Boolean
get() {
return this.result?.netResult != null
return this.netResult != null
}
// Manageable
@ -557,31 +683,29 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Start or continue a session
*/
fun startOrContinue() {
realm.executeTransaction {
when (val state = getState()) {
SessionState.PENDING, SessionState.PLANNED -> {
this.startDate = Date()
this.defineDefaultTournamentBuyinIfNecessary()
}
SessionState.PAUSED -> {
val pauseDate = this.pauseDate
if (pauseDate != null) {
this.breakDuration += Date().time - pauseDate.time
} else {
throw PAIllegalStateException("When resuming, the pause date must be set")
}
this.pauseDate = null
}
else -> {
throw PAIllegalStateException("unmanaged session state: $state")
when (val state = getState()) {
SessionState.PENDING, SessionState.PLANNED -> {
this.startDate = Date()
this.defineDefaultTournamentBuyinIfNecessary()
}
SessionState.PAUSED -> {
val pauseDate = this.pauseDate
if (pauseDate != null) {
this.breakDuration += Date().time - pauseDate.time
} else {
throw PAIllegalStateException("When resuming, the pause date must be set")
}
this.pauseDate = null
}
else -> {
throw PAIllegalStateException("unmanaged session state: $state")
}
}
}
private fun defineDefaultTournamentBuyinIfNecessary() {
if (this.tournamentEntryFee != null && this.result?.buyin == null) {
this.result?.buyin = this.tournamentEntryFee
if (this.tournamentEntryFee != null && this.buyin == null) {
this.buyin = this.tournamentEntryFee
}
}
@ -589,13 +713,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Pause a session
*/
fun pause() {
realm.executeTransaction {
when (val state = getState()) {
SessionState.STARTED -> {
this.pauseDate = Date()
}
else -> throw PAIllegalStateException("Pausing a session in an unmanaged state: $state")
when (val state = getState()) {
SessionState.STARTED -> {
this.pauseDate = Date()
}
else -> throw PAIllegalStateException("Pausing a session in an unmanaged state: $state")
}
}
@ -603,13 +725,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Stop a session
*/
fun stop(context: Context) {
realm.executeTransaction {
when (val state = getState()) {
SessionState.STARTED, SessionState.PAUSED -> {
this.end()
}
else -> throw Exception("Stopping session in unmanaged state: $state")
when (val state = getState()) {
SessionState.STARTED, SessionState.PAUSED -> {
this.end()
}
else -> throw Exception("Stopping session in unmanaged state: $state")
}
cancelStopNotification(context)
}
@ -618,12 +738,10 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
* Restart a session
*/
fun restart() {
realm.executeTransaction {
this.pauseDate = null
this.startDate = Date()
this.endDate = null
this.breakDuration = 0L
}
this.pauseDate = null
this.startDate = Date()
this.endDate = null
this.breakDuration = 0L
}
/**
@ -688,10 +806,11 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
if (isValid) {
// CrashLogging.log("Deletes session. Id = ${this.id}")
realm.executeTransaction {
// realm.executeTransaction {
cleanup()
deleteFromRealm()
}
// }
} else {
CrashLogging.log("Attempt to delete an invalid session")
}
@ -704,10 +823,10 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
// Updates the timeline
this.sessionSet?.let {
SessionSetManager.removeFromTimeline(this)
SessionManager.removeFromTimeline(this)
}
// cleanup unnecessary related objects
this.result?.deleteFromRealm()
// this.deleteFromRealm()
this.computableResults?.deleteAllFromRealm()
}
@ -738,7 +857,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
return "Session ${this.creationDate}"
}
override fun updateValue(value: Any?, row: RowRepresentable) {
fun updateValue(value: Any?, row: RowRepresentable, realm: Realm) {
when (row) {
SessionPropertiesRow.BANKROLL -> bankroll = value as Bankroll?
@ -757,38 +876,50 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
this.breakDuration = (value as Double? ?: 0.0).toLong() * 60 * 1000
}
SessionPropertiesRow.BUY_IN -> {
val localResult = getOrCreateResult()
localResult.buyin = value as Double?
// val localResult = getOrCreateResult()
this.buyin = value as Double?
}
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> {
val localResult = getOrCreateResult()
localResult.cashout = value as Double?
// val localResult = getOrCreateResult()
val cashOut = value as Double?
this.cashout = cashOut
if (cashOut != null) {
this.end()
}
}
SessionPropertiesRow.NET_RESULT -> {
val localResult = getOrCreateResult()
localResult.netResult = value as Double?
// val localResult = getOrCreateResult()
val netResult = value as Double?
this.netResult = netResult
if (netResult != null) {
this.end()
}
}
SessionPropertiesRow.COMMENT -> comment = value as String? ?: ""
SessionPropertiesRow.END_DATE -> if (value is Date?) {
this.endDate = value
}
SessionPropertiesRow.GAME -> {
if (value is ArrayList<*>) {
limit = try {
(value[0] as Int?)
} catch (e: Exception) {
null
when (value) {
is ArrayList<*> -> {
limit = try {
(value[0] as Int?)
} catch (e: Exception) {
null
}
game = try {
(value[1] as Game?)
} catch (e: Exception) {
null
}
}
game = try {
(value[1] as Game?)
} catch (e: Exception) {
null
is Game -> {
game = value
}
null -> {
limit = null
game = null
}
} else if (value is Game) {
game = value
} else if (value == null) {
limit = null
game = null
}
}
SessionPropertiesRow.INITIAL_BUY_IN -> {
@ -803,21 +934,21 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
}
SessionPropertiesRow.POSITION -> {
val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
// val localResult = if (result != null) result as Result else realm.createObject(Result::class.java)
if (value is Double) {
localResult.tournamentFinalPosition = value.toInt()
this.tournamentFinalPosition = value.toInt()
} else {
localResult.tournamentFinalPosition = null
this.tournamentFinalPosition = null
}
result = localResult
// result = localResult
}
SessionPropertiesRow.START_DATE -> if (value is Date) {
this.startDate = value
}
SessionPropertiesRow.TABLE_SIZE -> tableSize = value as Int?
SessionPropertiesRow.TIPS -> {
val localResult = getOrCreateResult()
localResult.tips = value as Double?
// val localResult = getOrCreateResult()
this.tips = value as Double?
}
SessionPropertiesRow.TOURNAMENT_NAME -> tournamentName = value as TournamentName?
SessionPropertiesRow.TOURNAMENT_TYPE -> tournamentType = (value as TournamentType?)?.ordinal
@ -842,6 +973,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
if (value != null) {
val customFieldEntry = CustomFieldEntry()
customFieldEntry.numericValue = value as Double?
customFieldEntry.customField = row
customFieldEntries.add(customFieldEntry)
row.entries.add(customFieldEntry)
}
@ -857,15 +989,26 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
private fun getOrCreateResult(): Result {
return this.result
?: run {
val result = realm.createObject(Result::class.java)
this.result = result
result
}
private fun customFieldEntries(realm: Realm, customField: CustomField): List<CustomFieldEntry> {
// val cfEntries = customField.entries
// val sessionEntries = this.customFieldEntries
//
// val entries = realm.where<CustomFieldEntry>()
// .`in`()
return listOf()
}
// private fun getOrCreateResult(): Result {
// return this.result
// ?: run {
// val result = realm.createObject(Result::class.java)
// this.result = result
// result
// }
// }
// Stat Entry
override fun entryTitle(context: Context): String {
@ -879,15 +1022,15 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
override fun formattedValue(stat: Stat): TextFormat {
this.result?.let { result ->
// this.result?.let { result ->
val value: Double? = when (stat) {
Stat.NET_RESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> result.net
Stat.NET_RESULT, Stat.AVERAGE, Stat.STANDARD_DEVIATION -> this.net
Stat.NUMBER_OF_GAMES, Stat.NUMBER_OF_SETS -> 1.0
Stat.AVERAGE_BUYIN -> result.buyin
Stat.AVERAGE_BUYIN -> this.buyin
Stat.ROI -> {
result.buyin?.let {
Stat.returnOnInvestment(result.net, it)
this.buyin?.let {
Stat.returnOnInvestment(this.net, it)
} ?: run {
null
}
@ -911,9 +1054,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
return TextFormat(NULL_TEXT)
}
} ?: run {
throw PAIllegalStateException("Asking for statIds on Session without Result")
}
// }
}
@ -971,19 +1112,19 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
SessionPropertiesRow.BANKROLL -> bankroll?.name ?: NULL_TEXT
SessionPropertiesRow.STAKES -> getFormattedStakes()
SessionPropertiesRow.BREAK_TIME -> if (this.breakDuration > 0.0) this.breakDuration.toMinutes() else NULL_TEXT
SessionPropertiesRow.BUY_IN -> this.result?.buyin?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> this.result?.cashout?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.NET_RESULT -> this.result?.netResult?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.BUY_IN -> this.buyin?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> this.cashout?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.NET_RESULT -> this.netResult?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.COMMENT -> if (this.comment.isNotEmpty()) this.comment else NULL_TEXT
SessionPropertiesRow.END_DATE -> this.endDate?.shortDateTime() ?: NULL_TEXT
SessionPropertiesRow.GAME -> getFormattedGame()
SessionPropertiesRow.INITIAL_BUY_IN -> tournamentEntryFee?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.LOCATION -> location?.name ?: NULL_TEXT
SessionPropertiesRow.PLAYERS -> tournamentNumberOfPlayers?.toString() ?: NULL_TEXT
SessionPropertiesRow.POSITION -> result?.tournamentFinalPosition?.toString() ?: NULL_TEXT
SessionPropertiesRow.POSITION -> this.tournamentFinalPosition?.toString() ?: NULL_TEXT
SessionPropertiesRow.START_DATE -> this.startDate?.shortDateTime() ?: NULL_TEXT
SessionPropertiesRow.TABLE_SIZE -> this.tableSize?.let { TableSize(it).localizedTitle(context) } ?: NULL_TEXT
SessionPropertiesRow.TIPS -> result?.tips?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.TIPS -> this.tips?.toCurrency(currency) ?: NULL_TEXT
SessionPropertiesRow.TOURNAMENT_TYPE -> {
this.tournamentType?.let {
TournamentType.values()[it].localizedTitle(context)
@ -1009,6 +1150,7 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
SessionPropertiesRow.HANDS_COUNT -> this.handsCountFormatted(context)
SessionPropertiesRow.NUMBER_OF_TABLES -> this.numberOfTables.toString()
is CustomField -> {
Timber.d("entries count = ${customFieldEntries.size}")
customFieldEntries.find { it.customField?.id == row.id }?.let { customFieldEntry ->
return customFieldEntry.getFormattedValue(currency)
}
@ -1024,12 +1166,12 @@ open class Session : RealmObject(), Savable, RowUpdatable, RowRepresentable, Tim
}
fun clearBuyinCashedOut() {
this.result?.buyin = null
this.result?.cashout = null
this.buyin = null
this.cashout = null
}
fun clearNetResult() {
this.result?.netResult = null
this.netResult = null
}
private fun cleanupBlinds(blinds: String?): String? {

@ -2,15 +2,18 @@ package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import io.realm.RealmResults
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.util.UUID_SEPARATOR
import net.pokeranalytics.android.util.extensions.findById
import timber.log.Timber
import java.util.*
open class UserConfig : RealmObject() {
companion object {
// these values are buffered to avoid the use of Realm instance
fun getConfiguration(realm: Realm): UserConfig {
realm.where(UserConfig::class.java).findFirst()?.let { config ->
return config
@ -29,8 +32,12 @@ open class UserConfig : RealmObject() {
var transactionTypeIds: String = ""
fun setTransactionTypeIds(transactionTypes: Set<TransactionType>) {
this.transactionTypeIds = transactionTypes.joinToString(UUID_SEPARATOR) { it.id }
// fun setTransactionTypeIds(transactionTypes: Set<TransactionType>) {
// this.transactionTypeIds = transactionTypes.joinToString(UUID_SEPARATOR) { it.id }
// }
fun setTransactionTypeIds(transactionTypeIds: Set<String>) {
this.transactionTypeIds = transactionTypeIds.joinToString(UUID_SEPARATOR)
}
fun transactionTypes(realm: Realm): List<TransactionType> {
@ -38,4 +45,40 @@ open class UserConfig : RealmObject() {
return ids.mapNotNull { realm.findById(it) }
}
}
}
object UserConfigObserver {
private var userConfig: RealmResults<UserConfig>? = null
var liveDealtHandsPerHour: Int = 0
var onlineDealtHandsPerHour: Int = 0
init {
val realm = Realm.getDefaultInstance()
this.updateValues(realm)
this.userConfig = realm.where(UserConfig::class.java).findAll()
this.userConfig?.addChangeListener { results ->
this.updateValues(results.realm)
val userConfig = UserConfig.getConfiguration(results.realm)
this.liveDealtHandsPerHour = userConfig.liveDealtHandsPerHour
this.onlineDealtHandsPerHour = userConfig.onlineDealtHandsPerHour
}
realm.close()
}
fun create() { }
private fun updateValues(realm: Realm) {
val userConfig = UserConfig.getConfiguration(realm)
this.liveDealtHandsPerHour = userConfig.liveDealtHandsPerHour
this.onlineDealtHandsPerHour = userConfig.onlineDealtHandsPerHour
Timber.d("UserConfigObserver values updated: live = ${this.liveDealtHandsPerHour}, online = ${this.onlineDealtHandsPerHour}")
}
}

@ -313,13 +313,7 @@ open class HandHistory : RealmObject(), Deletable, RowRepresentable, Filterable,
* Creates and affect a PlayerSetup at the given [positionIndex]
*/
fun createPlayerSetup(positionIndex: Int): PlayerSetup {
val playerSetup = if (this.realm != null) {
this.realm.createObject(PlayerSetup::class.java) }
else {
PlayerSetup()
}
val playerSetup = PlayerSetup()
playerSetup.position = positionIndex
this.playerSetups.add(playerSetup)
return playerSetup

@ -73,15 +73,20 @@ class FavoriteSessionFinder {
/**
* Copies the favorite session parameters on the [session]
*/
fun copyParametersFromFavoriteSession(session: Session, location: Location?, context: Context) {
fun copyParametersFromFavoriteSession(session: Session, location: Location?, context: Context, realm: Realm) {
val favoriteSession = favoriteSession(session.type, location, session.realm, context)
val favoriteSession = favoriteSession(session.type, location, realm, context)
favoriteSession?.let { fav ->
session.limit = fav.limit
session.game = fav.game
session.bankroll = fav.bankroll
fav.game?.let { game ->
session.game = realm.copyFromRealm(game)
}
fav.bankroll?.let { br ->
session.bankroll = realm.copyFromRealm(br)
}
session.tableSize = fav.tableSize
when (session.type) {

@ -0,0 +1,273 @@
package net.pokeranalytics.android.model.utils
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
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.SessionSet
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import kotlin.math.max
class CorruptSessionSetException(message: String) : Exception(message)
/**
* The manager is in charge of updating the abstract concept of timeline,
* representing the sequenced time frames where the user plays.
*/
object SessionManager {
private var sessions: RealmResults<Session>? = null
// private var results: RealmResults<Result>? = null
private var dateModifiedSessionIds: MutableSet<String> = mutableSetOf()
private var netModifiedSessionIds: MutableSet<String> = mutableSetOf()
private var statsToComputeSessionIds: MutableSet<String> = mutableSetOf()
init {
val realm = Realm.getDefaultInstance()
this.sessions = realm.where(Session::class.java).findAll()
this.sessions?.addChangeListener { results ->
if (this.dateModifiedSessionIds.isNotEmpty()) {
results.realm.writeAsync { asyncRealm ->
for (sessionId in dateModifiedSessionIds) {
asyncRealm.findById<Session>(sessionId)?.let { session ->
if (session.endDate != null) {
this.updateTimeline(session)
} else if (session.sessionSet != null) {
this.removeFromTimeline(session)
}
}
}
this.dateModifiedSessionIds.clear()
}
}
if (this.netModifiedSessionIds.isNotEmpty()) {
results.realm.writeAsync { asyncRealm ->
for (sessionId in netModifiedSessionIds) {
asyncRealm.findById<Session>(sessionId)?.computeNet(false)
}
this.dateModifiedSessionIds.clear()
}
}
if (this.statsToComputeSessionIds.isNotEmpty()) {
results.realm.writeAsync { asyncRealm ->
for (sessionId in statsToComputeSessionIds) {
asyncRealm.findById<Session>(sessionId)?.computeStats()
}
this.statsToComputeSessionIds.clear()
}
}
}
realm.close()
}
fun create() {
// empty, juste called to make sure the singleton is initialized
}
fun sessionDateChanged(session: Session) {
this.dateModifiedSessionIds.add(session.id)
}
fun sessionNetChanged(session: Session) {
this.netModifiedSessionIds.add(session.id)
}
fun sessionToCompute(session: Session) {
this.statsToComputeSessionIds.add(session.id)
}
/**
* Updates the global timeline using the updated [session]
*/
fun updateTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
if (session.startDate == null) {
throw ModelException("Start date should never be null here")
}
if (session.endDate == null) {
throw ModelException("End date should never be null here")
}
val sessionSets = this.matchingSets(session)
cleanupSessionSets(session, sessionSets)
}
private fun matchingSets(session: Session): RealmResults<SessionSet> {
val realm = session.realm
val endDate = session.endDate!! // tested above
val startDate = session.startDate!!
val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java)
query
.lessThanOrEqualTo("startDate", startDate)
.greaterThanOrEqualTo("endDate", startDate)
.or()
.lessThanOrEqualTo("startDate", endDate)
.greaterThanOrEqualTo("endDate", endDate)
.or()
.greaterThanOrEqualTo("startDate", startDate)
.lessThanOrEqualTo("endDate", endDate)
return query.findAll()
}
/**
* Multiple session sets update:
* Merges or splits session sets
* Does that by deleting then recreating
*/
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) {
// get all endedSessions from sets
val allImpactedSessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) }
}
allImpactedSessions.add(session)
// delete all sets
sessionSets.deleteAllFromRealm()
allImpactedSessions.forEach { impactedSession ->
val sets = matchingSets(impactedSession)
this.updateTimeFrames(sets, impactedSession)
}
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Update the global timeline using the impacted [sessionSets] and the updated [session]
*/
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) {
when (sessionSets.size) {
0 -> this.createOrUpdateSessionSet(session)
else -> this.mergeSessionGroups(session, sessionSets)
}
}
/**
* Creates or update the session set for the [session]
*/
private fun createOrUpdateSessionSet(session: Session) {
val set = session.sessionSet
if (set != null) {
set.startDate = session.startDate!! // tested above
set.endDate = session.endDate!!
} else {
this.createSessionSet(session)
}
}
/**
* Create a set and affect it to the [session]
*/
private fun createSessionSet(session: Session) {
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = session.startDate!!
set.endDate = session.endDate!!
set.breakDuration = session.breakDuration
session.sessionSet = set
set.computeStats()
}
/**
* Multiple session sets update:
* Merges all sets into one (delete all then create a new one)
*/
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) {
var startDate = session.startDate!!
var endDate = session.endDate!!
// get all endedSessions from sets
val sessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { sessions.addAll(it) }
}
// find earlier and later dates from all sets
sessions.forEach { s ->
if (s.startDate != null && s.endDate != null) {
val start = s.startDate!!
val end = s.endDate!!
if (start.before(startDate)) {
startDate = start
}
if (end.after(endDate)) {
endDate = end
}
} else {
throw CorruptSessionSetException("Set contains unfinished sessions!")
}
}
// delete all sets
sessionSets.deleteAllFromRealm()
// Create a new set
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = startDate
set.endDate = endDate
// Add the session linked to this timeframe to the new sessionGroup
session.sessionSet = set
// Add all orphan endedSessions
sessions.forEach { s ->
s.sessionSet = set
set.breakDuration = max(set.breakDuration, s.breakDuration)
}
set.computeStats()
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Removes the [session] from the timeline
*/
fun removeFromTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
val sessionSet = session.sessionSet
if (sessionSet != null) {
val sessions = mutableSetOf<Session>()
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) }
sessions.remove(session)
sessionSet.deleteFromRealm()
sessions.forEach {
updateTimeline(it)
}
}
}
}

@ -1,209 +0,0 @@
package net.pokeranalytics.android.model.utils
import io.realm.RealmQuery
import io.realm.RealmResults
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.SessionSet
import kotlin.math.max
class CorruptSessionSetException(message: String) : Exception(message)
/**
* The manager is in charge of updating the abstract concept of timeline,
* representing the sequenced time frames where the user plays.
*/
class SessionSetManager {
companion object {
/**
* Updates the global timeline using the updated [session]
*/
fun updateTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
if (session.startDate == null) {
throw ModelException("Start date should never be null here")
}
if (session.endDate == null) {
throw ModelException("End date should never be null here")
}
val sessionSets = this.matchingSets(session)
cleanupSessionSets(session, sessionSets)
}
private fun matchingSets(session: Session) : RealmResults<SessionSet> {
val realm = session.realm
val endDate = session.endDate!! // tested above
val startDate = session.startDate!!
val query: RealmQuery<SessionSet> = realm.where(SessionSet::class.java)
query
.lessThanOrEqualTo("startDate", startDate)
.greaterThanOrEqualTo("endDate", startDate)
.or()
.lessThanOrEqualTo("startDate", endDate)
.greaterThanOrEqualTo("endDate", endDate)
.or()
.greaterThanOrEqualTo("startDate", startDate)
.lessThanOrEqualTo("endDate", endDate)
return query.findAll()
}
/**
* Multiple session sets update:
* Merges or splits session sets
* Does that by deleting then recreating
*/
private fun cleanupSessionSets(session: Session, sessionSets: RealmResults<SessionSet>) {
// get all endedSessions from sets
val allImpactedSessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { allImpactedSessions.addAll(it) }
}
allImpactedSessions.add(session)
// delete all sets
sessionSets.deleteAllFromRealm()
allImpactedSessions.forEach { impactedSession ->
val sets = matchingSets(impactedSession)
this.updateTimeFrames(sets, impactedSession)
}
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Update the global timeline using the impacted [sessionSets] and the updated [session]
*/
private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, session: Session) {
when (sessionSets.size) {
0 -> this.createOrUpdateSessionSet(session)
else -> this.mergeSessionGroups(session, sessionSets)
}
}
/**
* Creates or update the session set for the [session]
*/
private fun createOrUpdateSessionSet(session: Session) {
val set = session.sessionSet
if (set != null) {
set.startDate = session.startDate!! // tested above
set.endDate = session.endDate!!
} else {
this.createSessionSet(session)
}
}
/**
* Create a set and affect it to the [session]
*/
private fun createSessionSet(session: Session) {
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = session.startDate!!
set.endDate = session.endDate!!
set.breakDuration = session.breakDuration
session.sessionSet = set
set.computeStats()
}
/**
* Multiple session sets update:
* Merges all sets into one (delete all then create a new one)
*/
private fun mergeSessionGroups(session: Session, sessionSets: RealmResults<SessionSet>) {
var startDate = session.startDate!!
var endDate = session.endDate!!
// get all endedSessions from sets
val sessions = mutableSetOf<Session>()
sessionSets.forEach { set ->
set.sessions?.asIterable()?.let { sessions.addAll(it) }
}
// find earlier and later dates from all sets
sessions.forEach { s ->
if (s.startDate != null && s.endDate != null) {
val start = s.startDate!!
val end = s.endDate!!
if (start.before(startDate)) {
startDate = start
}
if (end.after(endDate)) {
endDate = end
}
} else {
throw CorruptSessionSetException("Set contains unfinished sessions!")
}
}
// delete all sets
sessionSets.deleteAllFromRealm()
// Create a new set
val set: SessionSet = SessionSet.newInstance(session.realm)
set.startDate = startDate
set.endDate = endDate
// Add the session linked to this timeframe to the new sessionGroup
session.sessionSet = set
// Add all orphan endedSessions
sessions.forEach { s ->
s.sessionSet = set
set.breakDuration = max(set.breakDuration, s.breakDuration)
}
set.computeStats()
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}")
}
/**
* Removes the [session] from the timeline
*/
fun removeFromTimeline(session: Session) {
if (!session.realm.isInTransaction) {
throw PAIllegalStateException("realm should be in transaction at this point")
}
val sessionSet = session.sessionSet
if (sessionSet != null) {
val sessions = mutableSetOf<Session>()
sessionSet.sessions?.asIterable()?.let { sessions.addAll(it) }
sessions.remove(session)
sessionSet.deleteFromRealm()
sessions.forEach {
updateTimeline(it)
}
}
}
}
}

@ -21,6 +21,7 @@ import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.extensions.findAll
import net.pokeranalytics.android.util.extensions.isSameMonth
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
enum class Tab(var identifier: Int) {
@ -114,11 +115,15 @@ class HomeActivity : BaseActivity(), NewPerformanceListener {
// observe currency changes
this.currencies = realm.where(Currency::class.java).findAll()
this.currencies.addChangeListener { currencies, _ ->
realm.executeTransaction {
currencies.forEach {
it.refreshRelatedRatedValues()
this.currencies.addChangeListener { _, _ ->
realm.writeAsync { asyncRealm ->
val currencies = asyncRealm.where(Currency::class.java).findAll()
currencies.forEach { currency ->
currency.refreshRelatedRatedValues()
}
}
}
}

@ -9,14 +9,13 @@ import android.provider.MediaStore
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.util.ImageUtils
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.util.*
open class MediaActivity : BaseActivity() {
@ -52,12 +51,12 @@ open class MediaActivity : BaseActivity() {
val filesList = ArrayList<File>()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
if (tempFile != null) {
tempFile?.let {
GlobalScope.launch(Dispatchers.Main) {
filesList.add(it)
CoroutineScope(context = Dispatchers.Main).launch {
filesList.add(it)
getPictures(filesList)
}
}
@ -65,7 +64,7 @@ open class MediaActivity : BaseActivity() {
data.clipData?.let { clipData ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -78,7 +77,7 @@ open class MediaActivity : BaseActivity() {
filesList.add(photoFile)
}
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
getPictures(filesList)
}
@ -90,7 +89,7 @@ open class MediaActivity : BaseActivity() {
data.data?.let { uri ->
try {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
isLoadingNewPictures()
}
@ -98,7 +97,7 @@ open class MediaActivity : BaseActivity() {
val photoFile = ImageUtils.createTempImageFile(this@MediaActivity)
ImageUtils.copyInputStreamToFile(inputStream!!, photoFile)
filesList.add(photoFile)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
getPictures(filesList)
}
@ -176,13 +175,6 @@ open class MediaActivity : BaseActivity() {
}
}
// // Test if we have the permission
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// selectedChoice = SELECTED_CHOICE_TAKE_PICTURE
// askForStoragePermission()
// return
// }
}
@ -206,21 +198,6 @@ open class MediaActivity : BaseActivity() {
}
}
// Test if we have the permission
// if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// selectedChoice = SELECTED_CHOICE_SELECT_PICTURE
// askForStoragePermission()
// return
// }
//
// this.multiplePictures = multiplePictures
//
// val galleryIntent = Intent()
// galleryIntent.type = "image/*"
// galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiplePictures)
// galleryIntent.action = Intent.ACTION_GET_CONTENT
// startActivityForResult(galleryIntent, REQUEST_CODE_SELECT_PICTURE)
}
/**

@ -8,7 +8,7 @@ import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -98,7 +98,7 @@ class ImportFragment : RealmFragment(), ImportDelegate {
CoroutineScope(coroutineContext).launch {
val coroutine = GlobalScope.async {
val coroutine = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> Start Import...")

@ -50,6 +50,7 @@ import net.pokeranalytics.android.util.billing.IAPProducts
import net.pokeranalytics.android.util.billing.PurchaseListener
import net.pokeranalytics.android.util.csv.ProductCSVDescriptors
import net.pokeranalytics.android.util.extensions.dateTimeFileFormatted
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import java.io.File
import java.io.IOException
@ -187,17 +188,17 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
private fun updateMainCurrency(currencyCode: String, rate: Double) {
Preferences.setCurrencyCode(currencyCode, requireContext())
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(Currency::class.java).findAll().forEach { currency ->
getRealm().writeAsync { asyncRealm ->
asyncRealm.where(Currency::class.java).findAll().forEach { currency ->
currency.rate = (currency.rate ?: 1.0) * rate
}
realm.where(Session::class.java).findAll().forEach { session ->
asyncRealm.where(Session::class.java).findAll().forEach { session ->
session.bankrollHasBeenUpdated()
}
}
realm.close()
settingsAdapterRow.refreshRow(SettingsRow.CURRENCY)
}

@ -9,7 +9,7 @@ import android.view.*
import androidx.appcompat.widget.Toolbar
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -160,7 +160,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
CoroutineScope(coroutineContext).launch {
val async = GlobalScope.async {
val async = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> start...")
@ -222,7 +222,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
Stat.AVERAGE_BUYIN
)
if (QueryCondition.IsCash.queryWith(realm.where(Result::class.java).isNotNull("tips")).count() > 0) {
if (QueryCondition.IsCash.queryWith(realm.where(Session::class.java).isNotNull("tips")).count() > 0) {
cgStats.add(Stat.TOTAL_TIPS)
}
@ -235,7 +235,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener {
Stat.NUMBER_OF_GAMES,
Stat.AVERAGE_BUYIN
)
if (QueryCondition.IsTournament.queryWith(realm.where(Result::class.java).isNotNull("tips")).count() > 0) {
if (QueryCondition.IsTournament.queryWith(realm.where(Session::class.java).isNotNull("tips")).count() > 0) {
tStats.add(Stat.TOTAL_TIPS)
}

@ -8,15 +8,16 @@ import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.google.android.material.snackbar.Snackbar
import io.realm.RealmObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.util.extensions.writeAsync
/**
* Deletable Item Fragment
@ -56,7 +57,7 @@ abstract class DeletableItemFragment : RealmFragment() {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
deleteItem(dataListAdapter, deletableItems(), id)
}
@ -88,8 +89,8 @@ abstract class DeletableItemFragment : RealmFragment() {
if (itemToDelete.isValidForDelete(this.getRealm())) {
deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition
getRealm().executeTransaction {
itemToDelete.deleteDependencies(it)
getRealm().writeAsync { asyncRealm ->
itemToDelete.deleteDependencies(asyncRealm)
itemToDelete.deleteFromRealm()
}
itemHasBeenReInserted = false
@ -117,9 +118,9 @@ abstract class DeletableItemFragment : RealmFragment() {
snackBar?.setAction(R.string.cancel) {
if (!itemHasBeenReInserted) {
itemHasBeenReInserted = true
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
deletedItem?.let {
val item = realm.copyToRealmOrUpdate(it)
val item = asyncRealm.copyToRealmOrUpdate(it)
updateUIAfterUndoDeletion(item)
}
}

@ -9,8 +9,8 @@ import android.view.*
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -140,7 +140,7 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
viewGroup.removeAllViews()
viewGroup.addView(layoutCurrentFilter)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
viewGroup.visibility = View.VISIBLE
}
@ -153,7 +153,7 @@ open class FilterableFragment : RealmFragment(), FilterHandler {
*/
private fun hideSelectedFilter() {
view?.findViewById<ViewGroup>(R.id.selectedFilter).let {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
it?.visibility = View.GONE
}
}

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.core.view.get
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.chip.Chip
import io.realm.RealmObject
import net.pokeranalytics.android.databinding.BottomSheetGameListBinding
import net.pokeranalytics.android.model.Limit
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
@ -38,10 +39,11 @@ class BottomSheetListGameFragment : BottomSheetListFragment() {
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
this.model.realmData?.let {
val selectedData = it[position]
selectedData?.let { data ->
this.model.someValues[1] = data
this.model.realmData?.let { results ->
val selectedData = results[position]
(selectedData as? RealmObject)?.let { data ->
val obj = results.realm.copyFromRealm(data)
this.model.someValues[1] = obj
this.onRowValueChanged()
// this.delegate.onRowValueChanged(values, this.row)
dismiss()

@ -17,6 +17,7 @@ import net.pokeranalytics.android.ui.modules.data.DataManagerFragment
import net.pokeranalytics.android.ui.viewmodel.ReportViewModel
import net.pokeranalytics.android.ui.viewmodel.ViewModelHolder
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
abstract class AbstractReportFragment : DataManagerFragment() {
@ -84,7 +85,7 @@ abstract class AbstractReportFragment : DataManagerFragment() {
saveReport(nameEditText.text.toString())
dialog.dismiss()
} catch (e: Exception) {
Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_LONG).show()
}
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
@ -105,36 +106,34 @@ abstract class AbstractReportFragment : DataManagerFragment() {
}
this.reportViewModel.title = name
val rs = this.model.item as ReportSetup
getRealm().executeTransaction { realm ->
val firstSave = (this.model.primaryKey == null)
if (firstSave) {
val options = this.selectedReport.options
rs.name = name
rs.display = this.reportViewModel.reportDisplay?.ordinal ?: throw PAIllegalStateException("Display not set")
options.stats.forEach {
rs.statIds.add(it.uniqueIdentifier)
}
options.criterias.forEach { criteria ->
when (criteria) {
is CustomFieldCriteria -> rs.criteriaCustomFieldIds.add(criteria.customFieldId)
else -> rs.criteriaIds.add(criteria.uniqueIdentifier)
}
getRealm().writeAsync { asyncRealm ->
val rs = this.model.item as ReportSetup
val options = this.selectedReport.options
rs.name = name
rs.display = this.reportViewModel.reportDisplay?.ordinal
?: throw PAIllegalStateException("Display not set")
rs.statIds.clear()
rs.statIds.addAll(options.stats.map { it.uniqueIdentifier })
rs.criteriaIds.clear()
options.criterias.forEach { criteria ->
when (criteria) {
is CustomFieldCriteria -> rs.criteriaCustomFieldIds.add(criteria.customFieldId)
else -> rs.criteriaIds.add(criteria.uniqueIdentifier)
}
}
options.filterId?.let { id ->
rs.filter = realm.findById(id)
}
realm.copyToRealmOrUpdate(rs)
} else {
rs.name = name
realm.insertOrUpdate(rs)
options.filterId?.let { id ->
rs.filter = asyncRealm.findById(id)
}
asyncRealm.copyToRealmOrUpdate(rs)
this.model.primaryKey = rs.id
}
this.model.primaryKey = rs.id
this.deleteButtonShouldAppear = true
setToolbarTitle(this.reportViewModel.title)
}

@ -8,7 +8,7 @@ import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -203,10 +203,10 @@ open class ComposableTableReportFragment : RealmFragment(), StaticRowRepresentab
showLoader()
GlobalScope.launch(coroutineContext) {
CoroutineScope(context = Dispatchers.IO).launch(coroutineContext) {
var report: Report? = null
val test = GlobalScope.async {
val test = CoroutineScope(context = Dispatchers.IO).async {
val s = Date()
Timber.d(">>> start...")

@ -11,8 +11,8 @@ import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.AggregationType
@ -163,7 +163,7 @@ class ProgressReportFragment : AbstractReportFragment() {
graphContainer.hideWithAnimation()
progressBar.showWithAnimation()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val s = Date()
Timber.d(">>> start...")

@ -9,8 +9,8 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.mikephil.charting.data.LineDataSet
import io.realm.RealmResults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -35,8 +35,6 @@ import net.pokeranalytics.android.ui.view.rows.BankrollTotalRow
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.util.extensions.sorted
import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
interface BankrollRowRepresentable : RowRepresentable {
var bankrollId: String?
@ -88,7 +86,7 @@ class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSour
if (requestCode == RequestCode.BANKROLL_DETAILS.value && resultCode == Activity.RESULT_OK) {
val itemToDeleteId = data?.getStringExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName)
itemToDeleteId?.let { id ->
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
deleteItem(dataListAdapter, bankrolls, id)

@ -13,8 +13,8 @@ import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.LineDataSet
import com.google.android.material.tabs.TabLayout
import io.realm.Realm
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
@ -167,7 +167,7 @@ class CalendarDetailsFragment : BaseFragment(), StaticRowRepresentableDataSource
this.model.computedResults?.let { computedResults ->
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val startDate = Date()

@ -11,7 +11,6 @@ import io.realm.Realm
import io.realm.RealmModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.calculus.Calculator
@ -348,7 +347,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
binding.progressBar.showWithAnimation()
binding.recyclerView.hideWithAnimation()
GlobalScope.launch {
CoroutineScope(context = Dispatchers.IO).launch {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -357,7 +356,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
realm.close()
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
displayData()
}
}

@ -12,7 +12,6 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.extensions.*
import java.util.*
import kotlin.collections.HashMap
class GridCalendarViewModel : ViewModel(), RowRepresentableDataSource {
@ -64,8 +63,8 @@ class GridCalendarViewModel : ViewModel(), RowRepresentableDataSource {
while (tmpDate.time <= lastDate.time) {
val result = groupedSessions[tmpDate]?.let { bucket ->
if (bucket.sumByDouble {
it.result?.net ?: 0.0
if (bucket.sumOf {
it?.net ?: 0.0
} > 0.0) CellResult.POSITIVE else CellResult.NEGATIVE
} ?: run {
CellResult.EMPTY

@ -16,6 +16,8 @@ import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
open class DataManagerFragment : RealmFragment() {
@ -96,13 +98,26 @@ open class DataManagerFragment : RealmFragment() {
val status = savable.getSaveValidityStatus(realm = this.getRealm())
when (status) {
SaveValidityStatus.VALID -> {
this.getRealm().executeTransaction {
val managedItem = it.copyToRealmOrUpdate(this.model.item)
if (managedItem is Savable) {
val uniqueIdentifier = managedItem.id
finishActivityWithResult(uniqueIdentifier)
}
val realm = getRealm()
val item = this.model.item
Timber.d("save")
realm.writeAsync { asyncRealm ->
Timber.d("execute async")
asyncRealm.copyToRealmOrUpdate(item)
}
if (item is Savable) {
val uniqueIdentifier = item.id
finishActivityWithResult(uniqueIdentifier)
}
// this.getRealm().executeTransaction {
// val managedItem = it.copyToRealmOrUpdate(this.model.item)
// if (managedItem is Savable) {
// val uniqueIdentifier = managedItem.id
// finishActivityWithResult(uniqueIdentifier)
// }
// }
onDataSaved()
}
else -> {

@ -11,12 +11,13 @@ import io.realm.RealmModel
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.util.CrashLogging
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.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowUpdatable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.writeAsync
open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegate {
@ -67,16 +68,25 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
}
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
getRealm().executeTransaction {
(this.model.item as RowUpdatable).updateValue(value, row)
this.writeIfPossible(value, row)
rowRepresentableAdapter.refreshRow(row)
}
private fun writeIfPossible(value: Any?, row: RowRepresentable) {
if (!this.isUpdating) {
return
}
getRealm().writeAsync { asyncRealm ->
try {
(this.model.item as RowUpdatable).updateValue(value, row)
asyncRealm.copyToRealmOrUpdate(this.model.item)
} catch (e: Exception) {
CrashLogging.log("Exception caught: row = $row, value=$value, class=${this.javaClass}")
throw e
}
}
rowRepresentableAdapter.refreshRow(row)
}
/**

@ -13,8 +13,8 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -215,7 +215,7 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
when (row) {
is Comment -> {
if (row.isValidForDelete(getRealm())) {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(300)
showAlertDialog(requireContext(), messageResId = R.string.are_you_sure_you_want_to_delete, showCancelButton = true, positiveAction = {
playerModel.deleteComment(row)

@ -16,6 +16,7 @@ import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.isSameDay
import net.pokeranalytics.android.util.extensions.mediumDate
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
class PlayerDataViewModel : DataManagerViewModel(), StaticRowRepresentableDataSource {
@ -131,9 +132,11 @@ class PlayerDataViewModel : DataManagerViewModel(), StaticRowRepresentableDataSo
*/
fun cleanupComments() { // called when saving the custom field
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
this.commentsToDelete.forEach { // entries are out of realm
realm.where<Comment>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
val ids = this.commentsToDelete.map { it.id }
realm.writeAsync { asyncRealm ->
ids.forEach { id -> // entries are out of realm
asyncRealm.where<Comment>().equalTo("id", id).findFirst()?.deleteFromRealm()
}
}
realm.close()

@ -2,8 +2,8 @@ package net.pokeranalytics.android.ui.modules.data
import android.content.Context
import io.realm.kotlin.where
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.calculus.bankroll.BankrollReportManager
@ -141,7 +141,7 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
when (val next = rows[index + 1]) {
TransactionPropertiesRow.DATE, TransactionPropertiesRow.COMMENT -> {}
else -> {
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(200)
onRowSelected(0, next)
}

@ -39,12 +39,13 @@ import net.pokeranalytics.android.ui.modules.filter.FiltersActivity
import net.pokeranalytics.android.ui.modules.handhistory.HandHistoryActivity
import net.pokeranalytics.android.ui.modules.session.SessionActivity
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.URL
import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.billing.PurchaseListener
import net.pokeranalytics.android.util.extensions.count
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import java.util.*
class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseListener {
@ -494,9 +495,14 @@ class FeedFragment : FilterableFragment(), RowRepresentableDelegate, PurchaseLis
* Delete selected transaction
*/
private fun deleteSelectedTransaction() {
getRealm().executeTransaction {
selectedTransaction?.deleteFromRealm()
this.selectedTransaction?.id?.let { transactionId ->
getRealm().writeAsync { asyncRealm ->
val transaction = asyncRealm.findById<Transaction>(transactionId)
transaction?.deleteFromRealm()
}
}
selectedTransactionPosition = -1
}

@ -9,8 +9,8 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewAnimationUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.databinding.ActivityNewDataBinding
@ -105,7 +105,8 @@ class NewDataMenuActivity : BaseActivity() {
val intent = Intent()
intent.putExtra(IntentKey.CHOICE.keyName, choice)
setResult(RESULT_OK, intent)
GlobalScope.launch(Dispatchers.Main) {
CoroutineScope(context = Dispatchers.Main).launch {
delay(200)
hideMenu()
}

@ -240,11 +240,11 @@ open class FilterDetailsFragment : RealmFragment(), RowRepresentableDelegate {
this.activityModel.selectedCategoryRow?.let { category ->
getRealm().executeTransaction {
// getRealm().executeTransaction {
currentFilter?.remove(category)
val validConditions = this.model.selectedRows.filter { it.queryCondition != null }
currentFilter?.createOrUpdateFilterConditions(validConditions)
}
// }
}
currentFilter?.filterConditions?.forEach {

@ -4,11 +4,12 @@ import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import io.realm.Realm
import io.realm.kotlin.where
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.enumerations.IntIdentifiable
import net.pokeranalytics.android.util.enumerations.IntSearchable
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
enum class FilterActivityRequestCode {
SELECT_FILTER,
@ -45,10 +46,9 @@ interface FilterHandler {
fun saveFilter(context: Context, filterId: String) {
Preferences.setActiveFilterId(filterId, context)
val realm = Realm.getDefaultInstance()
realm.executeTransaction { executeRealm ->
currentFilter(context, executeRealm)?.let {
realm.writeAsync { asyncRealm ->
currentFilter(context, asyncRealm)?.let {
it.useCount++
}
}
@ -61,8 +61,9 @@ interface FilterHandler {
}
fun currentFilter(context: Context, realm: Realm): Filter? {
return Preferences.getActiveFilterId(context)?.let {
realm.where<Filter>().equalTo("id", it).findFirst()
return Preferences.getActiveFilterId(context)?.let { id ->
realm.findById(id)
// realm.where<Filter>().equalTo("id", it).findFirst()
} ?: run {
null
}

@ -15,12 +15,12 @@ import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rows.FilterCategoryRow
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.sorted
import net.pokeranalytics.android.util.extensions.writeAsync
open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
@ -218,16 +218,13 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
* Validate the updates of the queryWith
*/
private fun validateUpdates() {
val currentFilter = this.model.currentFilter
getRealm().executeTransaction { realm ->
currentFilter?.let {
it.name = it.query.getName(requireContext())
realm.copyToRealmOrUpdate(it)
}
}
val filterId = currentFilter?.id ?: ""
val filter = this.model.currentFilter
filter?.let { currentFilter ->
getRealm().writeAsync { asyncRealm ->
asyncRealm.copyToRealmOrUpdate(currentFilter)
}
}
val filterId = filter?.id ?: ""
finishActivityWithResult(filterId)
}
@ -239,9 +236,9 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate {
val filterCopy = this.model.filterCopy
val filterId = filterCopy?.id ?: ""
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
filterCopy?.let {
realm.copyToRealmOrUpdate(it)
asyncRealm.copyToRealmOrUpdate(it)
}
}
finishActivityWithResult(filterId)

@ -3,11 +3,14 @@ package net.pokeranalytics.android.ui.modules.filter
import android.app.Activity
import android.content.Context
import android.content.Intent
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.modules.datalist.DataListFragment
import net.pokeranalytics.android.ui.modules.filter.FilterHandler.Companion.INTENT_FILTER_UPDATE_FILTER_UI
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
open class FiltersListFragment : DataListFragment() {
@ -15,12 +18,19 @@ open class FiltersListFragment : DataListFragment() {
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
when (row) {
is Filter -> {
getRealm().executeTransaction {
row.updateValue(value, row)
}
val index = this.model.items.indexOf(row)
this.dataListAdapter.notifyItemChanged(index)
updateFilterUIIfNecessary(requireContext(), row.id)
val filterId = row.id
// val filter = row.realm.copyFromRealm(row)
// row.updateValue(value, row)
getRealm().writeAsync ({ asyncRealm ->
asyncRealm.findById<Filter>(filterId)?.let { filter ->
filter.updateValue(value, filter)
// asyncRealm.copyToRealmOrUpdate(filter)
} ?: throw PAIllegalStateException("missing filter: $filterId")
}, {
val index = this.model.items.indexOf(row)
this.dataListAdapter.notifyItemChanged(index)
updateFilterUIIfNecessary(requireContext(), row.id)
} )
}
else -> super.onRowValueChanged(value, row)
}

@ -35,6 +35,7 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
@ -203,7 +204,8 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen
val playerId = data?.getStringExtra(BaseFragment.BundleKey.PRIMARY_KEY.value)
?: throw PAIllegalStateException("Primary key not set where as activity has finished")
getRealm().findById<Player>(playerId)?.let { player ->
this.model.playerSelected(player)
val unmanagedPlayer = player.realm.copyFromRealm(player)
this.model.playerSelected(unmanagedPlayer)
} ?: throw PAIllegalStateException("Player (id=$playerId) not found")
this.editorAdapter.notifyDataSetChanged()
}
@ -684,10 +686,9 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen
*/
private fun deleteHand() {
getRealm().findById<HandHistory>(this.model.handHistory.id)?.let { hh ->
getRealm().executeTransaction {
hh.deleteFromRealm()
}
getRealm().writeAsync { asyncRealm ->
val hh = asyncRealm.findById<HandHistory>(this.model.handHistory.id)
hh?.deleteFromRealm()
}
this.activity?.finish()

@ -5,10 +5,6 @@ import android.text.InputType
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import io.realm.Realm
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.handhistory.HandSetup
@ -27,12 +23,11 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.util.CrashLogging
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.formatted
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import java.text.DecimalFormat
import java.text.ParseException
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KClass
enum class HHKeyboard {
@ -49,9 +44,6 @@ interface PlayerSetupCreationListener {
class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer, ActionListListener {
private val coroutineContext: CoroutineContext
get() = Dispatchers.Main
/***
* The hand history
*/
@ -641,33 +633,33 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
* Saves the current hand state in the database
*/
fun save(realm: Realm) {
realm.executeTransaction {
this.handHistory.actions.clear()
val actions = this.sortedActions.map { it.action }
this.handHistory.actions.addAll(actions)
if (!this.handHistory.isManaged) {
realm.copyToRealmOrUpdate(this.handHistory)
}
}
this.defineWinnerPositions()
}
private fun defineWinnerPositions() {
this.handHistory.actions.clear()
val actions = this.sortedActions.map { it.action }
this.handHistory.actions.addAll(actions)
val hhId = this.handHistory.id
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
}
realm.close()
}
c.await()
realm.writeAsync { asyncRealm ->
this.handHistory.defineWinnerPositions()
asyncRealm.copyToRealmOrUpdate(this.handHistory)
}
}
// this.defineWinnerPositions()
}
// private fun defineWinnerPositions() {
//
// val hhId = this.handHistory.id
// GlobalScope.launch(coroutineContext) {
// val c = GlobalScope.async {
// val realm = Realm.getDefaultInstance()
// realm.executeTransaction {
// realm.findById<HandHistory>(hhId)?.defineWinnerPositions()
// }
// realm.close()
// }
// c.await()
// }
//
// }
// Card Centralizer
@ -906,8 +898,8 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
fun changeStraddleSelection(positions: LinkedHashSet<Position>) {
if (positions.isEmpty()) {
this.firstStraddlePosition = null
this.handSetup.clearStraddles()
this.firstStraddlePosition = null
this.handSetup.clearStraddles()
} else {
if (this.firstStraddlePosition == null) {
@ -1013,16 +1005,16 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
fun playerSelected(player: Player) {
// Remove all use of the selected player
this.handHistory.playerSetups.filter { it.player == player }.forEach {
this.handHistory.playerSetups.filter { it.player?.id == player.id }.forEach {
it.player = null
}
// Affects the player to the selected position
this.tappedPlayerPositionIndex?.let { positionIndex ->
player.realm.executeTransaction {
// player.realm.executeTransaction {
val ps = this.handHistory.playerSetupForPosition(positionIndex) ?: this.handHistory.createPlayerSetup(positionIndex)
ps.player = player
}
// }
} ?: throw PAIllegalStateException("Click position not set for player selection")
}
@ -1036,20 +1028,20 @@ class EditorViewModel : ViewModel(), RowRepresentableDataSource, CardCentralizer
/***
* Tries to deletes the row at the given [position]
*/
fun deleteIfPossible(position: Int) {
when (val row = this.rowRepresentables[position]) {
is PlayerSetupRow -> {
val playerSetup = this.handHistory.playerSetupForPosition(row.positionIndex) ?: throw PAIllegalStateException("Attempt to delete an null object")
this.handHistory.playerSetups.remove(playerSetup)
this.handHistory.realm?.let {
it.executeTransaction {
playerSetup.deleteFromRealm()
}
}
}
}
}
// fun deleteIfPossible(position: Int) {
// when (val row = this.rowRepresentables[position]) {
// is PlayerSetupRow -> {
// val playerSetup = this.handHistory.playerSetupForPosition(row.positionIndex) ?: throw PAIllegalStateException("Attempt to delete an null object")
// this.handHistory.playerSetups.remove(playerSetup)
//
// this.handHistory.realm?.let {
// it.executeTransaction {
// playerSetup.deleteFromRealm()
// }
// }
// }
// }
// }
fun removePlayerSetup(positionIndex: Int) {
val ps = this.handHistory.playerSetupForPosition(positionIndex)

@ -13,7 +13,10 @@ import android.provider.MediaStore
import androidx.core.content.FileProvider
import com.arthenica.ffmpegkit.FFmpegKit
import io.realm.Realm
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
@ -70,8 +73,9 @@ class ReplayExportService : Service() {
private fun startGIFExport() {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val c = async {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -148,8 +152,8 @@ class ReplayExportService : Service() {
val start = Date().time
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val async = async {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")
@ -287,8 +291,8 @@ class ReplayExportService : Service() {
private fun startGIFExportPreQ() {
GlobalScope.launch(coroutineContext) {
val c = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val c = async {
val realm = Realm.getDefaultInstance()
realm.refresh()
@ -349,8 +353,8 @@ class ReplayExportService : Service() {
private fun startFFMPEGVideoExportPreQ() {
GlobalScope.launch(coroutineContext) {
val async = GlobalScope.async {
CoroutineScope(context = coroutineContext).launch {
val async = async {
val realm = Realm.getDefaultInstance()
val handHistory = realm.findById<HandHistory>(handHistoryId) ?: throw PAIllegalStateException("HandHistory not found, id: $handHistoryId")

@ -13,7 +13,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
@ -185,29 +185,31 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
if (sessionRealm != null) {
if (this.model.duplicate) { // duplicate session
realm.executeTransaction {
val session = sessionRealm.duplicate()
currentSession = session
realm.writeAsync { asyncRealm ->
asyncRealm.findById<Session>(sessionId)?.duplicate()?.let { duplicate ->
currentSession = asyncRealm.copyFromRealm(duplicate)
}
// val session = sessionRealm.duplicate()
// currentSession = session
}
sessionHasBeenUserCustomized = false
} else { // show existing session
currentSession = sessionRealm
currentSession = realm.copyFromRealm(sessionRealm)
sessionHasBeenUserCustomized = true
}
} else {
throw PAIllegalStateException("Session cannot be null here, session id = $sessionId")
}
} else { // create new session
realm.executeTransaction { executeRealm ->
currentSession = Session.newInstance(executeRealm, this.model.isTournament)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext())
}
currentSession = Session.newInstance(realm, this.model.isTournament)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, null, requireContext(), realm)
// Find the nearest location around the user
parentActivity?.findNearestLocation {
it?.let { location ->
realm.executeTransaction { executeRealm ->
val realmLocation = executeRealm.findById<Location>(location.id)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext())
realm.writeAsync { asyncRealm ->
val realmLocation = asyncRealm.findById<Location>(location.id)
FavoriteSessionFinder.copyParametersFromFavoriteSession(currentSession, realmLocation, requireContext(), asyncRealm)
currentSession.location = realmLocation
}
@ -217,6 +219,10 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
sessionHasBeenUserCustomized = false
}
// load everything and have some SessionManager links it all
//
}
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
@ -259,9 +265,8 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
this.sessionHasBeenUserCustomized = true
try {
getRealm().executeTransaction {
this.currentSession.updateValue(value, row)
}
this.currentSession.updateValue(value, row, getRealm())
this.writeChanges()
} catch (e: PAIllegalStateException) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
return
@ -279,6 +284,12 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
}
private fun writeChanges() {
getRealm().writeAsync { asyncRealm ->
asyncRealm.copyToRealmOrUpdate(this.currentSession)
}
}
/**
* Update the UI with the session data
* Should be called after the initialization of the session
@ -393,11 +404,14 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
}
currentSession.startOrContinue()
this.writeChanges()
binding.recyclerView.smoothScrollToPosition(0)
}
SessionState.STARTED -> {
currentSession.pause()
this.writeChanges()
}
else -> {
}
@ -405,6 +419,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
updateSessionUI()
}
private fun computeOptimalDuration() {
Timber.d("Start optimal duration finding attempt...")
@ -414,7 +429,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
var optimalDuration: Double? = null
val cr = GlobalScope.async {
val cr = CoroutineScope(context = Dispatchers.IO).async {
optimalDuration = CashGameOptimalDurationCalculator.start(isLive)
}
cr.await()
@ -482,7 +497,11 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
val bankrollId = this.currentSession.bankroll?.id
this.currentSession.delete()
val id = currentSession.id
getRealm().writeAsync { asyncRealm ->
asyncRealm.findById<Session>(id)?.delete()
}
bankrollId?.let {
BankrollReportManager.notifyBankrollReportImpact(bankrollId)
}
@ -495,7 +514,11 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun onBackPressed() {
super.onBackPressed()
if (!sessionHasBeenUserCustomized) {
currentSession.delete()
val id = currentSession.id
getRealm().writeAsync { asyncRealm ->
asyncRealm.findById<Session>(id)?.delete()
}
}
}
@ -593,18 +616,18 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
mapOf(
"bb" to session.cgBiggestBet,
"fee" to session.tournamentEntryFee,
"ratedBuyin" to session.result?.buyin
"ratedBuyin" to session.buyin
)
)
SessionPropertiesRow.BREAK_TIME -> row.editingDescriptors(mapOf())
SessionPropertiesRow.CASHED_OUT, SessionPropertiesRow.PRIZE -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.cashout
"defaultValue" to session.cashout
)
)
SessionPropertiesRow.NET_RESULT -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.netResult
"defaultValue" to session.netResult
)
)
SessionPropertiesRow.COMMENT -> row.editingDescriptors(
@ -624,7 +647,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
)
SessionPropertiesRow.POSITION -> row.editingDescriptors(
mapOf(
"defaultValue" to session.result?.tournamentFinalPosition
"defaultValue" to session.tournamentFinalPosition
)
)
SessionPropertiesRow.HANDS_COUNT -> row.editingDescriptors(
@ -641,7 +664,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
mapOf(
"sb" to session.cgBiggestBet?.round(),
"bb" to session.cgBiggestBet?.round(),
"tips" to session.result?.tips
"tips" to session.tips
)
)
is CustomField -> {
@ -663,16 +686,15 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepr
override fun resultCaptureTypeSelected(resultCaptureType: ResultCaptureType, applyBankroll: Boolean) {
getRealm().executeTransaction { // cleanup existing results
when (resultCaptureType) {
ResultCaptureType.NET_RESULT -> {
this.currentSession.clearBuyinCashedOut()
}
ResultCaptureType.BUYIN_CASHED_OUT -> {
this.currentSession.clearNetResult()
}
when (resultCaptureType) {
ResultCaptureType.NET_RESULT -> {
this.currentSession.clearBuyinCashedOut()
}
ResultCaptureType.BUYIN_CASHED_OUT -> {
this.currentSession.clearNetResult()
}
}
this.writeChanges()
this.model.resultCaptureType = resultCaptureType
if (applyBankroll) {

@ -17,7 +17,6 @@ import net.pokeranalytics.android.ui.view.rows.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rows.SeparatorRow
import net.pokeranalytics.android.ui.view.rows.SessionPropertiesRow
import net.pokeranalytics.android.util.extensions.sorted
import java.util.ArrayList
class SessionViewModel : ViewModel() {
@ -52,7 +51,7 @@ class SessionViewModel : ViewModel() {
fun updatedRowRepresentationForCurrentState(session: Session, realm: Realm, context: Context) {
val rows = ArrayList<RowRepresentable>()
val result = session.result
// val result = session.result
val currency = session.currency
// Headers
@ -62,7 +61,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = session.getFormattedDuration(),
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = session.currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = session.currency).textFormat
)
)
rows.add(SeparatorRow())
@ -72,7 +71,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
resId = R.string.pause,
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = currency).textFormat
)
)
rows.add(SeparatorRow())
@ -82,7 +81,7 @@ class SessionViewModel : ViewModel() {
CustomizableRowRepresentable(
RowViewType.HEADER_TITLE_AMOUNT_BIG,
title = session.getFormattedDuration(),
valueTextFormat = ComputedStat(Stat.NET_RESULT, result?.net ?: 0.0, currency = currency).textFormat
valueTextFormat = ComputedStat(Stat.NET_RESULT, session.net, currency = currency).textFormat
)
)
rows.add(

@ -10,6 +10,7 @@ import net.pokeranalytics.android.databinding.FragmentDealtHandsConfigBinding
import net.pokeranalytics.android.model.realm.ComputableResult
import net.pokeranalytics.android.model.realm.UserConfig
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.util.extensions.writeAsync
class DealtHandsPerHourFragment : RealmFragment() {
@ -57,19 +58,19 @@ class DealtHandsPerHourFragment : RealmFragment() {
private fun save() {
getRealm().executeTransaction { realm ->
getRealm().writeAsync { asyncRealm ->
val userConfig = UserConfig.getConfiguration(realm)
val userConfig = UserConfig.getConfiguration(asyncRealm)
this.liveValue.text.toString().toIntOrNull()?.let { liveDealtHandsPerHour ->
userConfig.liveDealtHandsPerHour = liveDealtHandsPerHour
}
this.onlineValue.text.toString().toIntOrNull()?.let { onlineDealtHandsPerHour ->
userConfig.onlineDealtHandsPerHour = onlineDealtHandsPerHour
}
realm.copyToRealmOrUpdate(userConfig)
asyncRealm.copyToRealmOrUpdate(userConfig)
// update all precomputed hand counts
realm.where(ComputableResult::class.java).findAll().forEach { cr ->
asyncRealm.where(ComputableResult::class.java).findAll().forEach { cr ->
cr.session?.let { session ->
cr.estimatedHands = session.estimatedHands
}

@ -16,6 +16,7 @@ import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
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.util.extensions.writeAsync
class TransactionFilterFragment : RealmFragment(), StaticRowRepresentableDataSource,
RowRepresentableDelegate {
@ -96,10 +97,13 @@ class TransactionFilterFragment : RealmFragment(), StaticRowRepresentableDataSou
}
private fun save() {
getRealm().executeTransaction { realm ->
val userConfig = UserConfig.getConfiguration(realm)
userConfig.setTransactionTypeIds(this.model.selectedTransactionTypes)
realm.copyToRealmOrUpdate(userConfig)
val ids = this.model.selectedTransactionTypes.map { it.id }
// this.model.selectedTransactionTypes.joinToString(UUID_SEPARATOR) { it.id }
getRealm().writeAsync { asyncRealm ->
val userConfig = UserConfig.getConfiguration(asyncRealm)
userConfig.setTransactionTypeIds(ids.toSet())
asyncRealm.copyToRealmOrUpdate(userConfig)
}
this.activity?.finish()
}

@ -511,9 +511,8 @@ enum class RowViewType(private var layoutRes: Int) : ViewIdentifier {
if (row is Session) {
itemView.findViewById<AppCompatTextView>(R.id.gameResult)?.let { gameResult ->
val result = row.result?.net ?: 0.0
val formattedStat =
ComputedStat(Stat.NET_RESULT, result, currency = row.currency).textFormat
ComputedStat(Stat.NET_RESULT, row.net, currency = row.currency).textFormat
gameResult.setTextFormat(formattedStat, itemView.context)
}

@ -147,10 +147,8 @@ class SessionRowView : FrameLayout {
this.infoIcon.isVisible = false
this.infoTitle.isVisible = false
session.result?.net?.let { netResult ->
val stat = ComputedStat(Stat.NET_RESULT, netResult, currency = session.currency)
this.gameResult.setTextFormat(stat.textFormat, context)
}
val stat = ComputedStat(Stat.NET_RESULT, session.net, currency = session.currency)
this.gameResult.setTextFormat(stat.textFormat, context)
// val formattedStat = ComputedStat(Stat.NET_RESULT, result, currency = session.currency).format()
}

@ -3,6 +3,7 @@ package net.pokeranalytics.android.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.RealmResults
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.exceptions.RowRepresentableEditDescriptorException
@ -211,17 +212,26 @@ class BottomSheetViewModel(var row: RowRepresentable) : ViewModel() {
}
return null
}
BottomSheetType.MULTI_SELECTION -> this.selectedRows
BottomSheetType.MULTI_SELECTION -> this.selectedRowsRealmCopy()
BottomSheetType.NUMERIC_TEXT -> this.doubleValue
BottomSheetType.GRID -> this.defaultSize
BottomSheetType.DOUBLE_LIST, BottomSheetType.LIST_GAME -> this.someValues
BottomSheetType.LIST_STATIC -> this.selectedRows.firstOrNull()
BottomSheetType.LIST_STATIC -> this.selectedRowsRealmCopy().firstOrNull()
BottomSheetType.SUM -> this.doubleValue
BottomSheetType.CASH_GAME_STAKES -> Stakes(this.secondStringValue, this.ante)
else -> null
}
}
private fun selectedRowsRealmCopy(): List<*> {
val realmObjects = this.selectedRows.filterIsInstance<RealmObject>()
return if (realmObjects.isNotEmpty()) {
realmObjects.map { it.realm.copyFromRealm(it) }
} else {
this.selectedRows
}
}
fun isSelected(row: RowRepresentable): Boolean {
return this.selectedRows.contains(row)
}
@ -243,9 +253,17 @@ class BottomSheetViewModel(var row: RowRepresentable) : ViewModel() {
}
fun rowSelected(position: Int): RowRepresentable? {
return when(this.row.bottomSheetType) {
BottomSheetType.LIST -> this.realmData?.get(position)
BottomSheetType.LIST_STATIC -> this.staticRows[position]
when(this.row.bottomSheetType) {
BottomSheetType.LIST -> {
this.realmData?.realm?.let { realm ->
val item = this.realmData?.get(position) as? RealmObject
item?.let {
return realm.copyFromRealm(it) as? RowRepresentable
}
}
return null
}
BottomSheetType.LIST_STATIC -> return this.staticRows[position]
else -> throw PAIllegalStateException("row selected for unmanaged bottom sheet type")
}
}

@ -8,6 +8,7 @@ import net.pokeranalytics.android.model.realm.Game
import net.pokeranalytics.android.model.realm.Location
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.extensions.getOrCreate
import net.pokeranalytics.android.util.extensions.writeAsync
import timber.log.Timber
import java.util.*
@ -35,9 +36,9 @@ class FakeDataManager {
val locations = realm.where<Location>().findAll()
if (locations.size == 0) {
realm.executeTransaction {
realm.writeAsync { asyncRealm ->
listOf("Bellagio", "Aria", "Borgata").map {
realm.getOrCreate<Location>(it)
asyncRealm.getOrCreate<Location>(it)
}
}
}
@ -82,10 +83,8 @@ class FakeDataManager {
session.tableSize = (2..10).random()
val buyin = buyinList.random()
session.result?.let { result ->
result.buyin = buyinList.random()
result.cashout = resultsList.random() + buyin
}
session.buyin = buyinList.random()
session.cashout = resultsList.random() + buyin
if (isTournament) {
session.tournamentEntryFee = buyin

@ -7,6 +7,7 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.ObjectIdentifier
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.util.extensions.findById
import net.pokeranalytics.android.util.extensions.writeAsync
import org.apache.commons.csv.CSVRecord
/**
@ -70,8 +71,8 @@ abstract class DataCSVDescriptor<T : Identifiable>(source: DataSource, vararg el
if (realm.isInTransaction) {
this.deleteInsertedFromRealm(realm)
} else {
realm.executeTransaction {
this.deleteInsertedFromRealm(realm)
realm.writeAsync { asyncRealm ->
this.deleteInsertedFromRealm(asyncRealm)
}
}

@ -26,7 +26,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
vararg elements: CSVField)
: DataCSVDescriptor<T>(source, *elements) {
var noSessionImport: Boolean = false
private var noSessionImport: Boolean = false
init {
val realm = Realm.getDefaultInstance()
@ -46,7 +46,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
protected fun parseSession(realm: Realm, record: CSVRecord, context: Context): Session? {
val isTournament = isTournament ?: false
val session = Session.newInstance(realm, isTournament, managed = false)
val session = Session.newInstance(realm, isTournament)
var startDate: Date? = null
var endDate: Date? = null
@ -127,13 +127,13 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
}
is SessionField.Buyin -> {
val buyin = field.parse(value)
session.result?.buyin = buyin
session.buyin = buyin
if (session.type == Session.Type.TOURNAMENT.ordinal) {
session.tournamentEntryFee = buyin
} else {}
}
is SessionField.CashedOut -> session.result?.cashout = field.parse(value)
is SessionField.NetResult -> session.result?.netResult = field.parse(value)
is SessionField.CashedOut -> session.cashout = field.parse(value)
is SessionField.NetResult -> session.netResult = field.parse(value)
is SessionField.SessionType -> {
Session.Type.getValueFromString(value)?.let { type ->
session.type = type.ordinal
@ -143,7 +143,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
is SessionField.NumberOfTables -> session.numberOfTables = field.parse(value) ?: 1
is SessionField.Addon -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Rebuy -> additionalBuyins += field.parse(value) ?: 0.0
is SessionField.Tips -> session.result?.tips = field.parse(value)
is SessionField.Tips -> session.tips = field.parse(value)
is SessionField.HandsCount -> session.handsCount = field.parse(value)
is SessionField.Break -> {
field.parse(value)?.let {
@ -191,7 +191,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
session.cgAnte = field.parse(value)
}
is SessionField.TableSize -> session.tableSize = TableSize.valueForLabel(value)
is SessionField.TournamentPosition -> session.result?.tournamentFinalPosition =
is SessionField.TournamentPosition -> session.tournamentFinalPosition =
field.parse(value)
is SessionField.TournamentName -> {
if (value.isNotEmpty()) {
@ -232,6 +232,7 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
Timber.d("N>> create: $number")
val entry = realm.copyToRealm(CustomFieldEntry())
entry.numericValue = number
entry.customField = customField
customField.entries.add(entry)
session.customFieldEntries.add(entry)
@ -255,10 +256,10 @@ abstract class PACSVDescriptor<T : Identifiable>(source: DataSource,
val bankroll = Bankroll.getOrCreate(realm, bankrollName, isLive, currencyCode, currencyRate)
session.bankroll = bankroll
session.result?.buyin?.let {
session.result?.buyin = it + additionalBuyins
session.buyin?.let {
session.buyin = it + additionalBuyins
}
val net = session.result?.net
val net = session.net
if (startDate != null && net != null) { // valid session
// session already in realm, we'd love not put it in Realm before doing the check

@ -46,10 +46,10 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.SessionType -> Session.Type.values()[data.type].value
is SessionField.Live -> field.format(data.isLive)
is SessionField.NumberOfTables -> field.format(data.numberOfTables)
is SessionField.Buyin -> field.format(data.result?.buyin)
is SessionField.CashedOut -> field.format(data.result?.cashout)
is SessionField.NetResult -> field.format(data.result?.netResult)
is SessionField.Tips -> field.format(data.result?.tips)
is SessionField.Buyin -> field.format(data.buyin)
is SessionField.CashedOut -> field.format(data.cashout)
is SessionField.NetResult -> field.format(data.netResult)
is SessionField.Tips -> field.format(data.tips)
is SessionField.HandsCount -> field.format(data.handsCount)
is SessionField.LimitType -> {
data.limit?.let { limit ->
@ -76,7 +76,7 @@ class SessionCSVDescriptor(source: DataSource, isTournament: Boolean?, vararg el
is SessionField.TournamentFeatures -> field.format(data.tournamentFeatures)
is SessionField.TournamentEntryFee -> field.format(data.tournamentEntryFee)
is SessionField.TournamentNumberOfPlayers -> field.format(data.tournamentNumberOfPlayers)
is SessionField.TournamentPosition -> field.format(data.result?.tournamentFinalPosition)
is SessionField.TournamentPosition -> field.format(data.tournamentFinalPosition)
is SessionField.Comment -> data.comment
is SessionField.NumberCustomField -> {
val entry = data.customFieldEntries.find { it.customField?.id == field.customField.id }

@ -7,6 +7,7 @@ import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.UsageCountable
import net.pokeranalytics.android.model.realm.*
import timber.log.Timber
fun <T : RealmModel>Realm.count(clazz: Class<T>) : Long {
return this.where(clazz).count()
@ -91,13 +92,43 @@ inline fun <reified C : RealmModel> Realm.sorted(editableOnly: Boolean = false,
return this.sorted(C::class.java, editableOnly, omitId = omitId)
}
fun Realm.writeAsync(handler: (Realm) -> (Unit)) {
Timber.d("Start write...")
this.executeTransactionAsync { asyncRealm ->
handler(asyncRealm)
}
// this.executeTransactionAsync({ asyncRealm ->
// handler(asyncRealm)
// }, { // success
//
// }, { error -> // error
// Timber.w("Realm write error: $error")
// })
}
fun Realm.writeAsync(handler: (Realm) -> (Unit), success: () -> (Unit)) {
Timber.d("Start write...")
this.executeTransactionAsync({ asyncRealm ->
handler(asyncRealm)
}, { // success
success()
}, { error -> // error
Timber.w("Realm write error: $error")
})
}
/**
* Updates the useCount variable of the CountableUsage entity
*/
fun <T : RealmModel>Realm.updateUsageCount(clazz: Class<T>) {
val results = this.where(clazz).findAll()
this.executeTransaction {
this.writeAsync { asyncRealm ->
val results = asyncRealm.where(clazz).findAll()
results.forEach { countableUsage ->
val countable = (countableUsage as UsageCountable)
@ -106,7 +137,7 @@ fun <T : RealmModel>Realm.updateUsageCount(clazz: Class<T>) {
TournamentFeature::class -> "tournamentFeatures.id"
else -> "${clazz.simpleName.decapitalize()}.id"
}
val count = it.where(countable.ownerClass).contains(fieldName, countable.id).count().toInt()
val count = asyncRealm.where(countable.ownerClass).contains(fieldName, countable.id).count().toInt()
countable.useCount = count
}
}

Loading…
Cancel
Save