Compare commits
33 Commits
@ -0,0 +1,56 @@ |
|||||||
|
package net.pokeranalytics.android |
||||||
|
|
||||||
|
import android.app.Service |
||||||
|
import android.content.Intent |
||||||
|
import android.os.Binder |
||||||
|
import android.os.IBinder |
||||||
|
import io.realm.Realm |
||||||
|
import timber.log.Timber |
||||||
|
|
||||||
|
class RealmWriteService : Service() { |
||||||
|
|
||||||
|
private lateinit var realm: Realm |
||||||
|
|
||||||
|
private val binder = LocalBinder() |
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder { |
||||||
|
return binder |
||||||
|
} |
||||||
|
|
||||||
|
inner class LocalBinder : Binder() { |
||||||
|
fun getService(): RealmWriteService = this@RealmWriteService |
||||||
|
} |
||||||
|
|
||||||
|
override fun onCreate() { |
||||||
|
super.onCreate() |
||||||
|
this.realm = Realm.getDefaultInstance() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onDestroy() { |
||||||
|
super.onDestroy() |
||||||
|
|
||||||
|
Timber.d(">>>> Service destroyed : realm close") |
||||||
|
this.realm.close() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { |
||||||
|
return super.onStartCommand(intent, flags, startId) |
||||||
|
} |
||||||
|
|
||||||
|
fun executeRealmAsyncTransaction(handler: (Realm) -> (Unit)) { |
||||||
|
|
||||||
|
Timber.d(">>>> Launch async transaction...") |
||||||
|
|
||||||
|
this.realm.executeTransactionAsync({ asyncRealm -> |
||||||
|
handler(asyncRealm) |
||||||
|
Timber.d(">> transaction handler done") |
||||||
|
}, { |
||||||
|
Timber.d(">> onSuccess, refreshing...") |
||||||
|
this.realm.refresh() |
||||||
|
}, { |
||||||
|
Timber.d(">> transaction failed: $it") |
||||||
|
}) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,61 @@ |
|||||||
|
package net.pokeranalytics.android.model.realm |
||||||
|
|
||||||
|
import io.realm.RealmObject |
||||||
|
import io.realm.RealmResults |
||||||
|
import io.realm.annotations.LinkingObjects |
||||||
|
import io.realm.annotations.PrimaryKey |
||||||
|
import io.realm.annotations.RealmClass |
||||||
|
import net.pokeranalytics.android.model.filter.Filterable |
||||||
|
import net.pokeranalytics.android.model.filter.QueryCondition |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
|
||||||
|
@RealmClass |
||||||
|
open class FlatTimeInterval : RealmObject(), Filterable { |
||||||
|
|
||||||
|
@PrimaryKey |
||||||
|
var id = UUID.randomUUID().toString() |
||||||
|
|
||||||
|
/** |
||||||
|
* The start date of the session |
||||||
|
*/ |
||||||
|
var startDate: Date = Date() |
||||||
|
set(value) { |
||||||
|
field = value |
||||||
|
this.computeDuration() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The start date of the session |
||||||
|
*/ |
||||||
|
var endDate: Date = Date() |
||||||
|
set(value) { |
||||||
|
field = value |
||||||
|
this.computeDuration() |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* the net duration of the session, automatically calculated |
||||||
|
*/ |
||||||
|
var duration: Long = 0L |
||||||
|
|
||||||
|
@LinkingObjects("flatTimeIntervals") |
||||||
|
val sessions: RealmResults<Session>? = null |
||||||
|
|
||||||
|
private fun computeDuration() { |
||||||
|
duration = endDate.time - startDate.time |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
fun fieldNameForQueryType(queryCondition: Class <out QueryCondition>): String? { |
||||||
|
Session.fieldNameForQueryType(queryCondition)?.let { |
||||||
|
return "sessions.$it" |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
@ -1,288 +0,0 @@ |
|||||||
//package net.pokeranalytics.android.model.realm |
|
||||||
// |
|
||||||
//import io.realm.RealmObject |
|
||||||
//import io.realm.RealmQuery |
|
||||||
//import io.realm.RealmResults |
|
||||||
//import io.realm.annotations.Ignore |
|
||||||
//import io.realm.annotations.LinkingObjects |
|
||||||
//import net.pokeranalytics.android.exceptions.ModelException |
|
||||||
//import timber.log.Timber |
|
||||||
//import java.util.* |
|
||||||
// |
|
||||||
//open class TimeFrame : RealmObject() { |
|
||||||
// |
|
||||||
// // A start date |
|
||||||
// var startDate: Date = Date() |
|
||||||
// private set(value) { |
|
||||||
// field = value |
|
||||||
// this.computeNetDuration() |
|
||||||
// } |
|
||||||
// |
|
||||||
// // An end date |
|
||||||
// var endDate: Date? = null |
|
||||||
// private set(value) { |
|
||||||
// field = value |
|
||||||
// this.computeNetDuration() |
|
||||||
// } |
|
||||||
// |
|
||||||
// // The latest pause date |
|
||||||
// var pauseDate: Date? = null |
|
||||||
// set(value) { |
|
||||||
// field?.let { |
|
||||||
// if (value == null && field != null) { |
|
||||||
// breakDuration += Date().time - it.time |
|
||||||
// } |
|
||||||
// } |
|
||||||
// field = value |
|
||||||
// this.computeNetDuration() |
|
||||||
// } |
|
||||||
// |
|
||||||
// // The break netDuration |
|
||||||
// var breakDuration: Long = 0L |
|
||||||
// set(value) { |
|
||||||
// field = value |
|
||||||
// this.computeNetDuration() |
|
||||||
// } |
|
||||||
// |
|
||||||
// // the total netDuration |
|
||||||
// var netDuration: Long = 0L |
|
||||||
// private set |
|
||||||
// |
|
||||||
// var hourlyDuration: Double = 0.0 |
|
||||||
// get() { |
|
||||||
// return this.netDuration / 3600000.0 // 3.6 millions of milliseconds |
|
||||||
// } |
|
||||||
// |
|
||||||
// // Session |
|
||||||
// @LinkingObjects("timeFrame") |
|
||||||
// private val endedSessions: RealmResults<Session>? = null // we should have only one session |
|
||||||
// |
|
||||||
// @Ignore |
|
||||||
// var session: Session? = null |
|
||||||
// get() = if (this.endedSessions != null && this.endedSessions.isEmpty()) null else this.endedSessions?.first() |
|
||||||
// |
|
||||||
// // Group |
|
||||||
// @LinkingObjects("timeFrame") |
|
||||||
// private val sets: RealmResults<SessionSet>? = null // we should have only one sessionGroup |
|
||||||
// |
|
||||||
// @Ignore |
|
||||||
// var set: SessionSet? = null |
|
||||||
// get() = this.sets?.first() |
|
||||||
// |
|
||||||
// fun setStart(startDate: Date) { |
|
||||||
// this.startDate = startDate |
|
||||||
// this.session?.let { |
|
||||||
// this.notifySessionDateChange(it) |
|
||||||
// } |
|
||||||
// } |
|
||||||
// |
|
||||||
// fun setEnd(endDate: Date?) { |
|
||||||
// this.endDate = endDate |
|
||||||
// this.session?.let { |
|
||||||
// this.notifySessionDateChange(it) |
|
||||||
// } |
|
||||||
// } |
|
||||||
// |
|
||||||
// fun setDate(startDate: Date, endDate: Date?) { |
|
||||||
// this.startDate = startDate |
|
||||||
// this.endDate = endDate |
|
||||||
// |
|
||||||
// this.session?.let { |
|
||||||
// this.notifySessionDateChange(it) |
|
||||||
// } |
|
||||||
// } |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Computes the net netDuration of the session |
|
||||||
// */ |
|
||||||
// private fun computeNetDuration() { |
|
||||||
// var endDate: Date = this.endDate ?: Date() |
|
||||||
// this.netDuration = endDate.time - this.startDate.time - this.breakDuration |
|
||||||
// } |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Queries all time frames that might be impacted by the date change |
|
||||||
// * Makes all necessary changes to keep sequential time frames |
|
||||||
// */ |
|
||||||
// fun notifySessionDateChange(owner: Session) { |
|
||||||
// |
|
||||||
// var query: RealmQuery<SessionSet> = this.realm.where(SessionSet::class.java) |
|
||||||
// query.isNotNull("timeFrame") |
|
||||||
// |
|
||||||
//// Timber.d("this> sd = : ${this.startDate}, ed = ${this.endDate}") |
|
||||||
// |
|
||||||
// val sets = realm.where(SessionSet::class.java).findAll() |
|
||||||
//// Timber.d("set count = ${sets.size}") |
|
||||||
// |
|
||||||
// if (this.endDate == null) { |
|
||||||
// query.greaterThanOrEqualTo("timeFrame.startDate", this.startDate) |
|
||||||
// .or() |
|
||||||
// .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) |
|
||||||
// .or() |
|
||||||
// .isNull("timeFrame.endDate") |
|
||||||
// } else { |
|
||||||
// val endDate = this.endDate!! |
|
||||||
// query |
|
||||||
// .lessThanOrEqualTo("timeFrame.startDate", this.startDate) |
|
||||||
// .greaterThanOrEqualTo("timeFrame.endDate", this.startDate) |
|
||||||
// .or() |
|
||||||
// .lessThanOrEqualTo("timeFrame.startDate", endDate) |
|
||||||
// .greaterThanOrEqualTo("timeFrame.endDate", endDate) |
|
||||||
// .or() |
|
||||||
// .greaterThanOrEqualTo("timeFrame.startDate", this.startDate) |
|
||||||
// .lessThanOrEqualTo("timeFrame.endDate", endDate) |
|
||||||
// .or() |
|
||||||
// .isNull("timeFrame.endDate") |
|
||||||
// .lessThanOrEqualTo("timeFrame.startDate", endDate) |
|
||||||
// } |
|
||||||
// |
|
||||||
// val sessionGroups = query.findAll() |
|
||||||
// |
|
||||||
// this.updateTimeFrames(sessionGroups, owner) |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Update Time frames from sets |
|
||||||
// */ |
|
||||||
// private fun updateTimeFrames(sessionSets: RealmResults<SessionSet>, owner: Session) { |
|
||||||
// |
|
||||||
// when (sessionSets.size) { |
|
||||||
// 0 -> this.createOrUpdateSessionSet(owner) |
|
||||||
// 1 -> this.updateSessionGroup(owner, sessionSets.first()!!) |
|
||||||
// else -> this.mergeSessionGroups(owner, sessionSets) |
|
||||||
// } |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Creates the session sessionGroup when the session has none |
|
||||||
// */ |
|
||||||
// private fun createOrUpdateSessionSet(owner: Session) { |
|
||||||
// |
|
||||||
// val set = owner.sessionSet |
|
||||||
// if (set != null) { |
|
||||||
// set.timeFrame?.startDate = this.startDate |
|
||||||
// set.timeFrame?.endDate = this.endDate |
|
||||||
// } else { |
|
||||||
// this.createSessionSet(owner) |
|
||||||
// } |
|
||||||
// |
|
||||||
//// Timber.d("sd = : ${set.timeFrame?.startDate}, ed = ${set.timeFrame?.endDate}") |
|
||||||
// Timber.d("netDuration 1 = : ${set?.timeFrame?.netDuration}") |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// fun createSessionSet(owner: Session) { |
|
||||||
// val set: SessionSet = SessionSet.newInstanceForResult(this.realm) |
|
||||||
// set.timeFrame?.let { |
|
||||||
// it.startDate = this.startDate |
|
||||||
// it.endDate = this.endDate |
|
||||||
// } ?: run { |
|
||||||
// throw ModelException("TimeFrame should never be null here") |
|
||||||
// } |
|
||||||
// |
|
||||||
// owner.sessionSet = set |
|
||||||
// } |
|
||||||
// |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Single SessionSet update, the session might be the owner |
|
||||||
// * Changes the sessionGroup timeframe using the current timeframe dates |
|
||||||
// */ |
|
||||||
// private fun updateSessionGroup(owner: Session, sessionSet: SessionSet) { |
|
||||||
// |
|
||||||
// var timeFrame: TimeFrame = sessionSet.timeFrame!! // tested in the query |
|
||||||
//// timeFrame.setDate(this.startDate, this.endDate) |
|
||||||
// |
|
||||||
// val sisterSessions = sessionSet.endedSessions!! // shouldn't crash ever |
|
||||||
// |
|
||||||
// // if we have only one session in the set and that it corresponds to the set |
|
||||||
// if (sessionSet.endedSessions?.size == 1 && sessionSet.endedSessions?.first() == owner) { |
|
||||||
// timeFrame.setDate(this.startDate, this.endDate) |
|
||||||
// } else { // there are 2+ endedSessions to manage and possible splits |
|
||||||
// |
|
||||||
// val endDate = this.endDate |
|
||||||
// |
|
||||||
// // case where all endedSessions are over but the set is not, we might have a split, so we delete the set and save everything again |
|
||||||
// if (endDate != null && sisterSessions.all { it.timeFrame?.endDate != null } && timeFrame.endDate == null) { |
|
||||||
// var endedSessions = mutableListOf<Session>(owner) |
|
||||||
// sessionSet.endedSessions?.forEach { endedSessions.add(it) } |
|
||||||
// sessionSet.deleteFromRealm() |
|
||||||
// endedSessions.forEach { it.timeFrame?.notifySessionDateChange(it) } |
|
||||||
// } else { |
|
||||||
// |
|
||||||
// if (this.startDate.before(timeFrame.startDate)) { |
|
||||||
// timeFrame.startDate = this.startDate |
|
||||||
// } |
|
||||||
// if (endDate != null && timeFrame.endDate != null && endDate.after(timeFrame.endDate)) { |
|
||||||
// timeFrame.endDate = endDate |
|
||||||
// } else if (endDate == null) { |
|
||||||
// timeFrame.endDate = null |
|
||||||
// } |
|
||||||
// |
|
||||||
// owner.sessionSet = sessionSet |
|
||||||
// |
|
||||||
//// Timber.d("sd = : ${sessionSet.timeFrame?.startDate}, ed = ${sessionSet.timeFrame?.endDate}") |
|
||||||
// Timber.d("netDuration 2 = : ${sessionSet.timeFrame?.netDuration}") |
|
||||||
// } |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// /** |
|
||||||
// * Multiple session sets update: |
|
||||||
// * Merges all sets into one (delete all then create a new one) |
|
||||||
// */ |
|
||||||
// private fun mergeSessionGroups(owner: Session, sessionSets: RealmResults<SessionSet>) { |
|
||||||
// |
|
||||||
// var startDate: Date = this.startDate |
|
||||||
// var endDate: Date? = this.endDate |
|
||||||
// |
|
||||||
// // find earlier and later dates from all sets |
|
||||||
// val timeFrames = sessionSets.mapNotNull { it.timeFrame } |
|
||||||
// timeFrames.forEach { tf -> |
|
||||||
// if (tf.startDate.before(startDate)) { |
|
||||||
// startDate = tf.startDate |
|
||||||
// } |
|
||||||
// |
|
||||||
// endDate?.let { ed -> |
|
||||||
// tf.endDate?.let { tfed -> |
|
||||||
// if (tfed.after(ed)) { |
|
||||||
// endDate = tfed |
|
||||||
// } |
|
||||||
// } |
|
||||||
// } ?: run { |
|
||||||
// endDate = tf.endDate |
|
||||||
// } |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
// // get all endedSessions from sets |
|
||||||
// var endedSessions = mutableSetOf<Session>() |
|
||||||
// sessionSets.forEach { set -> |
|
||||||
// set.endedSessions?.asIterable()?.let { endedSessions.addAll(it) } |
|
||||||
// } |
|
||||||
// |
|
||||||
// // delete all sets |
|
||||||
// sessionSets.deleteAllFromRealm() |
|
||||||
// |
|
||||||
// // Create a new sets |
|
||||||
// val set: SessionSet = SessionSet.newInstanceForResult(this.realm) |
|
||||||
// set.timeFrame?.let { |
|
||||||
// it.setDate(startDate, endDate) |
|
||||||
// } ?: run { |
|
||||||
// throw ModelException("TimeFrame should never be null here") |
|
||||||
// } |
|
||||||
// |
|
||||||
// // Add the session linked to this timeframe to the new sessionGroup |
|
||||||
// owner.sessionSet = set |
|
||||||
// |
|
||||||
// // Add all orphan endedSessions |
|
||||||
// endedSessions.forEach { it.sessionSet = set } |
|
||||||
// Timber.d("netDuration 3 = : ${set.timeFrame?.netDuration}") |
|
||||||
// |
|
||||||
// } |
|
||||||
// |
|
||||||
//} |
|
||||||
@ -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) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,534 @@ |
|||||||
|
package net.pokeranalytics.android.model.utils |
||||||
|
|
||||||
|
import io.realm.Realm |
||||||
|
import io.realm.RealmModel |
||||||
|
import io.realm.RealmQuery |
||||||
|
import io.realm.RealmResults |
||||||
|
import net.pokeranalytics.android.exceptions.ModelException |
||||||
|
import net.pokeranalytics.android.model.realm.FlatTimeInterval |
||||||
|
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.max |
||||||
|
import net.pokeranalytics.android.util.extensions.min |
||||||
|
import timber.log.Timber |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
class CorruptSessionSetException(message: String) : Exception(message) |
||||||
|
|
||||||
|
/** |
||||||
|
* The TimeManager pre-computes time related data: |
||||||
|
* - SessionSet: All overlapping sessions are grouped into a SessionSet, |
||||||
|
* used to calculate the number of sessions and break durations |
||||||
|
* - FlatTimeInterval: Sessions time intervals are breaked down into smaller intervals |
||||||
|
* when overlapping occurs to get faster duration calculations |
||||||
|
*/ |
||||||
|
object TimeManager { |
||||||
|
|
||||||
|
var sessions: RealmResults<Session>? = null |
||||||
|
|
||||||
|
private val sessionIdsToProcess = mutableSetOf<String>() |
||||||
|
|
||||||
|
private var start: Date? = null |
||||||
|
private var end: Date? = null |
||||||
|
|
||||||
|
fun configure() {} // launch init |
||||||
|
|
||||||
|
fun startChanged(session: Session, date: Date?) { |
||||||
|
this.start = min(this.start, date) |
||||||
|
this.end = max(this.end, session.endDate) |
||||||
|
this.sessionIdsToProcess.add(session.id) |
||||||
|
} |
||||||
|
|
||||||
|
fun endChanged(session: Session, date: Date?) { |
||||||
|
this.end = max(this.end, date) |
||||||
|
this.start = min(this.start, session.startDate) |
||||||
|
this.sessionIdsToProcess.add(session.id) |
||||||
|
} |
||||||
|
|
||||||
|
fun sessionDateChanged(session: Session) { |
||||||
|
this.start = min(this.start, session.startDate) |
||||||
|
this.end = max(this.end, session.endDate) |
||||||
|
this.sessionIdsToProcess.add(session.id) |
||||||
|
} |
||||||
|
|
||||||
|
init { |
||||||
|
|
||||||
|
val realm = Realm.getDefaultInstance() |
||||||
|
|
||||||
|
sessions = realm.where(Session::class.java).findAllAsync() |
||||||
|
sessions?.addChangeListener { _, _ -> |
||||||
|
|
||||||
|
if (sessionIdsToProcess.isNotEmpty()) { |
||||||
|
|
||||||
|
realm.executeTransactionAsync({ asyncRealm -> |
||||||
|
|
||||||
|
val sessions = sessionIdsToProcess.mapNotNull { asyncRealm.findById<Session>(it) } |
||||||
|
sessionIdsToProcess.clear() |
||||||
|
|
||||||
|
for (session in sessions) { |
||||||
|
Timber.d("Session id = ${session.id}") |
||||||
|
Timber.d("Session time intervals count = ${session.flatTimeIntervals.size}") |
||||||
|
session.flatTimeIntervals.deleteAllFromRealm() |
||||||
|
val fti = FlatTimeInterval() |
||||||
|
session.flatTimeIntervals.add(fti) |
||||||
|
asyncRealm.insertOrUpdate(session) |
||||||
|
} |
||||||
|
|
||||||
|
}, { |
||||||
|
Timber.d("executeTransactionAsync onSuccess listener...") |
||||||
|
val timeIntervals = realm.where(FlatTimeInterval::class.java).findAll() |
||||||
|
Timber.d("Total timeIntervals count = ${timeIntervals.size}") |
||||||
|
|
||||||
|
timeIntervals.forEach { |
||||||
|
Timber.d(">>> Time interval session count = ${it.sessions?.size}, session id = ${it.sessions?.firstOrNull()?.id}") |
||||||
|
} |
||||||
|
|
||||||
|
}, {}) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// sessions?.addChangeListener { _, _ -> |
||||||
|
// |
||||||
|
// Timber.d("...sessions change at ${Date().time}") |
||||||
|
// |
||||||
|
// val start = this.start |
||||||
|
// val end = this.end |
||||||
|
// if (start != null && end != null) { |
||||||
|
// |
||||||
|
// Timber.d("...process date changes from $start to $end") |
||||||
|
// |
||||||
|
// this.start = null |
||||||
|
// this.end = null |
||||||
|
// |
||||||
|
// realm.executeTransactionAsync ({ asyncRealm -> |
||||||
|
// processSessions(asyncRealm, start, end) |
||||||
|
// cleanUp() |
||||||
|
// }, { |
||||||
|
// Timber.d(">>>>> ON SUCCESS") |
||||||
|
// |
||||||
|
// realm.where(FlatTimeInterval::class.java).findAll().forEach { |
||||||
|
// Timber.d("######## sessions count = ${it.sessions?.size}") |
||||||
|
// } |
||||||
|
// |
||||||
|
// }, { |
||||||
|
// Timber.d("Transaction failed : $it") |
||||||
|
// }) |
||||||
|
// } |
||||||
|
// } |
||||||
|
|
||||||
|
realm.close() |
||||||
|
} |
||||||
|
|
||||||
|
private fun cleanUp() { |
||||||
|
this.start = null |
||||||
|
this.end = null |
||||||
|
this.sessionIdsToProcess.clear() |
||||||
|
} |
||||||
|
|
||||||
|
private fun processSessions(realm: Realm, start: Date, end: Date) { |
||||||
|
|
||||||
|
Timber.d("***** processSessions, process count = ${sessionIdsToProcess.size}") |
||||||
|
|
||||||
|
// val start = this.start |
||||||
|
// val end = this.end |
||||||
|
|
||||||
|
val sessions = sessionIdsToProcess.mapNotNull { realm.findById<Session>(it) } |
||||||
|
for (session in sessions) { |
||||||
|
|
||||||
|
// Session Sets |
||||||
|
val startDate = session.startDate |
||||||
|
val endDate = session.endDate |
||||||
|
if (startDate != null && endDate != null) { |
||||||
|
updateTimeline(session) |
||||||
|
} else if (session.sessionSet != null) { |
||||||
|
removeFromTimeline(session) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// FlatTimeIntervals |
||||||
|
processFlatTimeInterval(realm, sessions.toSet(), start, end) |
||||||
|
|
||||||
|
val ftis = realm.where(FlatTimeInterval::class.java).findAll() |
||||||
|
Timber.d("*** FTIs count = ${ftis.size}") |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 start = session.startDate!! |
||||||
|
val end = session.endDate!! |
||||||
|
|
||||||
|
val sessionSets = this.matchingData<SessionSet>(session.realm, start, end) |
||||||
|
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() |
||||||
|
// } |
||||||
|
|
||||||
|
private inline fun <reified T : RealmModel> matchingData(realm: Realm, startDate: Date, endDate: Date): RealmResults<T> { |
||||||
|
|
||||||
|
val query: RealmQuery<T> = realm.where(T::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 = matchingData<SessionSet>(impactedSession.realm, impactedSession.startDate!!, impactedSession.endDate!!) |
||||||
|
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.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.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.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) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun processFlatTimeInterval(realm: Realm, changedSessions: Set<Session>, start: Date, end: Date) { |
||||||
|
|
||||||
|
// Timber.d("***************************************************") |
||||||
|
// Timber.d("*** processFlatTimeInterval, from: $start, to $end") |
||||||
|
// Timber.d("***************************************************") |
||||||
|
|
||||||
|
val sessions = matchingData<Session>(realm, start, end) |
||||||
|
val intervalsStore = IntervalsStore(sessions.toSet()) |
||||||
|
intervalsStore.processSessions(changedSessions) |
||||||
|
|
||||||
|
Timber.d("*** sessions count = ${intervalsStore.sessions.size}") |
||||||
|
Timber.d("*** ftis to delete: ${intervalsStore.intervals.size}") |
||||||
|
for (fti in intervalsStore.intervals) { |
||||||
|
fti.deleteFromRealm() |
||||||
|
} |
||||||
|
|
||||||
|
// intervalsStore.intervals.forEach { it.deleteFromRealm() } |
||||||
|
|
||||||
|
val intervals = SessionInterval.intervalMap(intervalsStore.sessions) |
||||||
|
|
||||||
|
for (interval in intervals) { |
||||||
|
|
||||||
|
val sortedDates = interval.dates.sorted() |
||||||
|
for (i in (0 until sortedDates.size - 1)) { |
||||||
|
|
||||||
|
val s = sortedDates[i] |
||||||
|
val e = sortedDates[i + 1] |
||||||
|
|
||||||
|
val matchingSessions = interval.sessions.filter { |
||||||
|
val sd = it.startDate |
||||||
|
val ed = it.endDate |
||||||
|
(sd != null && ed != null && sd <= s && ed >= e) |
||||||
|
} |
||||||
|
if (matchingSessions.isNotEmpty()) { |
||||||
|
// Timber.d("**** Create FTI: $s - $e") |
||||||
|
val fti = FlatTimeInterval() |
||||||
|
fti.startDate = s |
||||||
|
fti.endDate = e |
||||||
|
for (session in matchingSessions) { |
||||||
|
session.flatTimeIntervals.add(fti) |
||||||
|
realm.insertOrUpdate(session) |
||||||
|
} |
||||||
|
realm.insertOrUpdate(fti) |
||||||
|
} else { |
||||||
|
Timber.w("The FTI has no sessions") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sessions.forEach { |
||||||
|
Timber.d("ending process...session FTI count = ${it.flatTimeIntervals.size}") |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class IntervalsStore(sessionSet: Set<Session>) { |
||||||
|
|
||||||
|
var start: Date = Date() |
||||||
|
var end: Date = Date(0L) |
||||||
|
|
||||||
|
val intervals = mutableSetOf<FlatTimeInterval>() |
||||||
|
|
||||||
|
val sessions = mutableSetOf<Session>() |
||||||
|
|
||||||
|
private val sessionIds: MutableSet<String> = mutableSetOf() |
||||||
|
|
||||||
|
init { |
||||||
|
processSessions(sessionSet) |
||||||
|
} |
||||||
|
|
||||||
|
fun processSessions(sessions: Set<Session>) { |
||||||
|
this.sessions.addAll(sessions) |
||||||
|
for (session in sessions) { |
||||||
|
// Timber.d("PROCESS > s = ${session.startDate} / e = ${session.endDate} ") |
||||||
|
loadIntervals(session) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun loadIntervals(session: Session) { |
||||||
|
|
||||||
|
if (sessionIds.contains(session.id)) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
session.startDate?.let { this.start = min(this.start, it) } |
||||||
|
session.endDate?.let { this.end = max(this.end, it) } |
||||||
|
|
||||||
|
this.sessionIds.add(session.id) |
||||||
|
|
||||||
|
Timber.d("session FTI count = ${session.flatTimeIntervals.size}") |
||||||
|
for (fti in session.flatTimeIntervals) { |
||||||
|
this.intervals.add(fti) |
||||||
|
|
||||||
|
fti.sessions?.let { sessions -> |
||||||
|
processSessions(sessions.toSet()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class SessionInterval(session: Session) { |
||||||
|
|
||||||
|
var start: Date |
||||||
|
var end: Date? |
||||||
|
|
||||||
|
var sessions: MutableSet<Session> = mutableSetOf() |
||||||
|
val dates: MutableSet<Date> = mutableSetOf() |
||||||
|
|
||||||
|
val duration: Long |
||||||
|
get() { |
||||||
|
val endDate = end ?: Date() |
||||||
|
return endDate.time - start.time |
||||||
|
} |
||||||
|
|
||||||
|
init { |
||||||
|
this.start = session.startDate!! |
||||||
|
this.end = session.endDate |
||||||
|
|
||||||
|
// Timber.d("INTERVAL init: s = $start, e = $end") |
||||||
|
|
||||||
|
this.addSession(session) |
||||||
|
} |
||||||
|
|
||||||
|
private fun addSession(session: Session) { |
||||||
|
this.sessions.add(session) |
||||||
|
|
||||||
|
session.startDate?.let { this.dates.add(it) } |
||||||
|
session.endDate?.let { endDate -> |
||||||
|
this.dates.add(endDate) |
||||||
|
if (endDate > end) { |
||||||
|
end = endDate |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
fun intervalMap(sessions: Set<Session>): List<SessionInterval> { |
||||||
|
|
||||||
|
val sorted = sessions.sortedBy { it.startDate } |
||||||
|
val intervals = mutableListOf<SessionInterval>() |
||||||
|
|
||||||
|
sorted.firstOrNull()?.let { firstSession -> |
||||||
|
|
||||||
|
var currentInterval = SessionInterval(firstSession) |
||||||
|
intervals.add(currentInterval) |
||||||
|
|
||||||
|
val remainingSessions = sorted.drop(1) |
||||||
|
for (session in remainingSessions) { |
||||||
|
val start = session.startDate!! |
||||||
|
val currentEnd = currentInterval.end |
||||||
|
if (currentEnd != null && start > currentEnd) { |
||||||
|
val interval = SessionInterval(session) |
||||||
|
currentInterval = interval |
||||||
|
intervals.add(interval) |
||||||
|
} else { |
||||||
|
currentInterval.addSession(session) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// intervals.forEach { |
||||||
|
// Timber.d("s = ${it.start}, e = ${it.end}") |
||||||
|
// } |
||||||
|
|
||||||
|
return intervals |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue