mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-14 21:18:51 -06:00
Creating the Habitgroup model
This commit is contained in:
@@ -79,6 +79,10 @@ class HabitsApplication : Application() {
|
|||||||
val habitList = component.habitList
|
val habitList = component.habitList
|
||||||
for (h in habitList) h.recompute()
|
for (h in habitList) h.recompute()
|
||||||
|
|
||||||
|
val habitGroupList = component.habitGroupList
|
||||||
|
for (hgr in habitGroupList) hgr.recompute()
|
||||||
|
habitGroupList.populateGroupsWith(habitList)
|
||||||
|
|
||||||
widgetUpdater = component.widgetUpdater.apply {
|
widgetUpdater = component.widgetUpdater.apply {
|
||||||
startListening()
|
startListening()
|
||||||
scheduleStartDayWidgetUpdate()
|
scheduleStartDayWidgetUpdate()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.isoron.uhabits.core.AppScope
|
|||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.io.GenericImporter
|
import org.isoron.uhabits.core.io.GenericImporter
|
||||||
import org.isoron.uhabits.core.io.Logging
|
import org.isoron.uhabits.core.io.Logging
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroupList
|
||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.ModelFactory
|
import org.isoron.uhabits.core.models.ModelFactory
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
@@ -50,6 +51,7 @@ interface HabitsApplicationComponent {
|
|||||||
val genericImporter: GenericImporter
|
val genericImporter: GenericImporter
|
||||||
val habitCardListCache: HabitCardListCache
|
val habitCardListCache: HabitCardListCache
|
||||||
val habitList: HabitList
|
val habitList: HabitList
|
||||||
|
val habitGroupList: HabitGroupList
|
||||||
val intentFactory: IntentFactory
|
val intentFactory: IntentFactory
|
||||||
val intentParser: IntentParser
|
val intentParser: IntentParser
|
||||||
val logging: Logging
|
val logging: Logging
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import org.isoron.uhabits.core.commands.CommandRunner
|
|||||||
import org.isoron.uhabits.core.database.Database
|
import org.isoron.uhabits.core.database.Database
|
||||||
import org.isoron.uhabits.core.database.DatabaseOpener
|
import org.isoron.uhabits.core.database.DatabaseOpener
|
||||||
import org.isoron.uhabits.core.io.Logging
|
import org.isoron.uhabits.core.io.Logging
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroupList
|
||||||
import org.isoron.uhabits.core.models.HabitList
|
import org.isoron.uhabits.core.models.HabitList
|
||||||
import org.isoron.uhabits.core.models.ModelFactory
|
import org.isoron.uhabits.core.models.ModelFactory
|
||||||
import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
|
import org.isoron.uhabits.core.models.sqlite.SQLModelFactory
|
||||||
|
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitGroupList
|
||||||
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
|
import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
import org.isoron.uhabits.core.preferences.WidgetPreferences
|
||||||
@@ -97,6 +99,12 @@ class HabitsModule(dbFile: File) {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@AppScope
|
||||||
|
fun getHabitGroupList(list: SQLiteHabitGroupList): HabitGroupList {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@AppScope
|
@AppScope
|
||||||
fun getDatabaseOpener(opener: AndroidDatabaseOpener): DatabaseOpener {
|
fun getDatabaseOpener(opener: AndroidDatabaseOpener): DatabaseOpener {
|
||||||
|
|||||||
@@ -40,12 +40,15 @@ data class Habit(
|
|||||||
val computedEntries: EntryList,
|
val computedEntries: EntryList,
|
||||||
val originalEntries: EntryList,
|
val originalEntries: EntryList,
|
||||||
val scores: ScoreList,
|
val scores: ScoreList,
|
||||||
val streaks: StreakList
|
val streaks: StreakList,
|
||||||
|
var parentID: Long? = null,
|
||||||
|
var parentUUID: String? = null
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
|
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parent: HabitGroup? = null
|
||||||
var observable = ModelObservable()
|
var observable = ModelObservable()
|
||||||
|
|
||||||
val isNumerical: Boolean
|
val isNumerical: Boolean
|
||||||
@@ -111,6 +114,14 @@ data class Habit(
|
|||||||
return computedEntries.getKnown().lastOrNull()?.timestamp ?: DateUtils.getTodayWithOffset()
|
return computedEntries.getKnown().lastOrNull()?.timestamp ?: DateUtils.getTodayWithOffset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hierarchyLevel(): Int {
|
||||||
|
return if (parentID == null) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1 + parent!!.hierarchyLevel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun copyFrom(other: Habit) {
|
fun copyFrom(other: Habit) {
|
||||||
this.color = other.color
|
this.color = other.color
|
||||||
this.description = other.description
|
this.description = other.description
|
||||||
@@ -127,6 +138,8 @@ data class Habit(
|
|||||||
this.type = other.type
|
this.type = other.type
|
||||||
this.unit = other.unit
|
this.unit = other.unit
|
||||||
this.uuid = other.uuid
|
this.uuid = other.uuid
|
||||||
|
this.parentID = other.parentID
|
||||||
|
this.parentUUID = other.parentUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@@ -148,6 +161,8 @@ data class Habit(
|
|||||||
if (type != other.type) return false
|
if (type != other.type) return false
|
||||||
if (unit != other.unit) return false
|
if (unit != other.unit) return false
|
||||||
if (uuid != other.uuid) return false
|
if (uuid != other.uuid) return false
|
||||||
|
if (parentID != other.parentID) return false
|
||||||
|
if (parentUUID != other.parentUUID) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -168,6 +183,8 @@ data class Habit(
|
|||||||
result = 31 * result + type.value
|
result = 31 * result + type.value
|
||||||
result = 31 * result + unit.hashCode()
|
result = 31 * result + unit.hashCode()
|
||||||
result = 31 * result + (uuid?.hashCode() ?: 0)
|
result = 31 * result + (uuid?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (parentID?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (parentUUID?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,19 @@ data class HabitGroup(
|
|||||||
var position: Int = 0,
|
var position: Int = 0,
|
||||||
var question: String = "",
|
var question: String = "",
|
||||||
var reminder: Reminder? = null,
|
var reminder: Reminder? = null,
|
||||||
var unit: String = "",
|
|
||||||
var uuid: String? = null,
|
var uuid: String? = null,
|
||||||
var habitList: HabitList,
|
var habitList: HabitList,
|
||||||
var habitGroupList: HabitGroupList,
|
var habitGroupList: HabitGroupList,
|
||||||
val scores: ScoreList,
|
val scores: ScoreList,
|
||||||
val streaks: StreakList
|
val streaks: StreakList,
|
||||||
|
var parentID: Long? = null,
|
||||||
|
var parentUUID: String? = null
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
|
if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parent: HabitGroup? = null
|
||||||
var observable = ModelObservable()
|
var observable = ModelObservable()
|
||||||
|
|
||||||
val uriString: String
|
val uriString: String
|
||||||
@@ -76,7 +78,7 @@ data class HabitGroup(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyFrom(other: Habit) {
|
fun copyFrom(other: HabitGroup) {
|
||||||
this.color = other.color
|
this.color = other.color
|
||||||
this.description = other.description
|
this.description = other.description
|
||||||
// this.id should not be copied
|
// this.id should not be copied
|
||||||
@@ -85,8 +87,9 @@ data class HabitGroup(
|
|||||||
this.position = other.position
|
this.position = other.position
|
||||||
this.question = other.question
|
this.question = other.question
|
||||||
this.reminder = other.reminder
|
this.reminder = other.reminder
|
||||||
this.unit = other.unit
|
|
||||||
this.uuid = other.uuid
|
this.uuid = other.uuid
|
||||||
|
this.parentID = other.parentID
|
||||||
|
this.parentUUID = other.parentUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@@ -101,8 +104,9 @@ data class HabitGroup(
|
|||||||
if (position != other.position) return false
|
if (position != other.position) return false
|
||||||
if (question != other.question) return false
|
if (question != other.question) return false
|
||||||
if (reminder != other.reminder) return false
|
if (reminder != other.reminder) return false
|
||||||
if (unit != other.unit) return false
|
|
||||||
if (uuid != other.uuid) return false
|
if (uuid != other.uuid) return false
|
||||||
|
if (parentID != other.parentID) return false
|
||||||
|
if (parentUUID != other.parentUUID) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -116,8 +120,37 @@ data class HabitGroup(
|
|||||||
result = 31 * result + position
|
result = 31 * result + position
|
||||||
result = 31 * result + question.hashCode()
|
result = 31 * result + question.hashCode()
|
||||||
result = 31 * result + (reminder?.hashCode() ?: 0)
|
result = 31 * result + (reminder?.hashCode() ?: 0)
|
||||||
result = 31 * result + unit.hashCode()
|
|
||||||
result = 31 * result + (uuid?.hashCode() ?: 0)
|
result = 31 * result + (uuid?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (parentID?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (parentUUID?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hierarchyLevel(): Int {
|
||||||
|
return if (parentID == null) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1 + parent!!.hierarchyLevel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHabitByUUIDDeep(uuid: String?): Habit? {
|
||||||
|
val habit = habitList.getByUUID(uuid)
|
||||||
|
if (habit != null) return habit
|
||||||
|
for (hgr in habitGroupList) {
|
||||||
|
val found = hgr.getHabitByUUIDDeep(uuid)
|
||||||
|
if (found != null) return found
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHabitGroupByUUIDDeep(uuid: String?): HabitGroup? {
|
||||||
|
val habitGroup = habitGroupList.getByUUID(uuid)
|
||||||
|
if (habitGroup != null) return habitGroup
|
||||||
|
for (hgr in habitGroupList) {
|
||||||
|
val found = hgr.getHabitGroupByUUIDDeep(uuid)
|
||||||
|
if (found != null) return found
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.isoron.uhabits.core.models
|
package org.isoron.uhabits.core.models
|
||||||
|
|
||||||
import com.opencsv.CSVWriter
|
import com.opencsv.CSVWriter
|
||||||
|
import org.isoron.uhabits.core.models.HabitList.Order
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Writer
|
import java.io.Writer
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
@@ -63,6 +64,30 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
|
|||||||
*/
|
*/
|
||||||
abstract fun getByUUID(uuid: String?): HabitGroup?
|
abstract fun getByUUID(uuid: String?): HabitGroup?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the habit with the specified UUID which is
|
||||||
|
* present at any hierarchy within this list.
|
||||||
|
*/
|
||||||
|
fun getHabitByUUIDDeep(uuid: String?): Habit? {
|
||||||
|
for (hgr in this) {
|
||||||
|
val habit = hgr.getHabitByUUIDDeep(uuid)
|
||||||
|
if (habit != null) {
|
||||||
|
return habit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHabitGroupByUUIDDeep(uuid: String?): HabitGroup? {
|
||||||
|
for (hgr in this) {
|
||||||
|
val habit = hgr.getHabitGroupByUUIDDeep(uuid)
|
||||||
|
if (habit != null) {
|
||||||
|
return habit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the habit that occupies a certain position.
|
* Returns the habit that occupies a certain position.
|
||||||
*
|
*
|
||||||
@@ -150,6 +175,42 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
|
|||||||
update(listOf(habitGroup))
|
update(listOf(habitGroup))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun populateGroupsWith(habitList: HabitList) {
|
||||||
|
val toRemove = mutableListOf<String?>()
|
||||||
|
for (habit in habitList) {
|
||||||
|
val hgr = getByUUID(habit.parentUUID)
|
||||||
|
if (hgr != null) {
|
||||||
|
hgr.habitList.add(habit)
|
||||||
|
habit.parent = hgr
|
||||||
|
toRemove.add(habit.uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uuid in toRemove) {
|
||||||
|
val h = habitList.getByUUID(uuid)
|
||||||
|
if (h != null) {
|
||||||
|
habitList.remove(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toRemove.clear()
|
||||||
|
for (hgr1 in this) {
|
||||||
|
val hgr2 = getByUUID(hgr1.parentUUID)
|
||||||
|
if (hgr2 != null) {
|
||||||
|
hgr2.habitGroupList.add(hgr1)
|
||||||
|
toRemove.add(hgr1.uuid)
|
||||||
|
hgr1.parent = hgr2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (uuid in toRemove) {
|
||||||
|
val h = getByUUID(uuid)
|
||||||
|
if (h != null) {
|
||||||
|
remove(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (hgr in this) {
|
||||||
|
hgr.recompute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the list of habits to the given writer, in CSV format. There is
|
* Writes the list of habits to the given writer, in CSV format. There is
|
||||||
* one line for each habit, containing the fields name, description,
|
* one line for each habit, containing the fields name, description,
|
||||||
@@ -186,15 +247,4 @@ abstract class HabitGroupList : Iterable<HabitGroup> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract fun resort()
|
abstract fun resort()
|
||||||
enum class Order {
|
|
||||||
BY_NAME_ASC,
|
|
||||||
BY_NAME_DESC,
|
|
||||||
BY_COLOR_ASC,
|
|
||||||
BY_COLOR_DESC,
|
|
||||||
BY_SCORE_ASC,
|
|
||||||
BY_SCORE_DESC,
|
|
||||||
BY_STATUS_ASC,
|
|
||||||
BY_STATUS_DESC,
|
|
||||||
BY_POSITION
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ data class HabitMatcher(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun matches(habitGroup: HabitGroup): Boolean {
|
||||||
|
if (!isArchivedAllowed && habitGroup.isArchived) return false
|
||||||
|
if (isReminderRequired && !habitGroup.hasReminder()) return false
|
||||||
|
if (!isCompletedAllowed && habitGroup.isCompletedToday()) return false
|
||||||
|
if (!isEnteredAllowed && habitGroup.isEnteredToday()) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val WITH_ALARM = HabitMatcher(
|
val WITH_ALARM = HabitMatcher(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package org.isoron.uhabits.core.models
|
|||||||
|
|
||||||
import org.isoron.uhabits.core.database.Repository
|
import org.isoron.uhabits.core.database.Repository
|
||||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||||
|
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
|
||||||
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,11 +39,25 @@ interface ModelFactory {
|
|||||||
computedEntries = buildComputedEntries()
|
computedEntries = buildComputedEntries()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fun buildHabitGroup(): HabitGroup {
|
||||||
|
val habits = buildHabitList()
|
||||||
|
val groups = buildHabitGroupList()
|
||||||
|
val scores = buildScoreList()
|
||||||
|
val streaks = buildStreakList()
|
||||||
|
return HabitGroup(
|
||||||
|
habitList = habits,
|
||||||
|
habitGroupList = groups,
|
||||||
|
scores = scores,
|
||||||
|
streaks = streaks
|
||||||
|
)
|
||||||
|
}
|
||||||
fun buildComputedEntries(): EntryList
|
fun buildComputedEntries(): EntryList
|
||||||
fun buildOriginalEntries(): EntryList
|
fun buildOriginalEntries(): EntryList
|
||||||
fun buildHabitList(): HabitList
|
fun buildHabitList(): HabitList
|
||||||
|
fun buildHabitGroupList(): HabitGroupList
|
||||||
fun buildScoreList(): ScoreList
|
fun buildScoreList(): ScoreList
|
||||||
fun buildStreakList(): StreakList
|
fun buildStreakList(): StreakList
|
||||||
fun buildHabitListRepository(): Repository<HabitRecord>
|
fun buildHabitListRepository(): Repository<HabitRecord>
|
||||||
fun buildRepetitionListRepository(): Repository<EntryRecord>
|
fun buildRepetitionListRepository(): Repository<EntryRecord>
|
||||||
|
fun buildHabitGroupListRepository(): Repository<HabitGroupRecord>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
package org.isoron.uhabits.core.models.memory
|
||||||
|
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroup
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroupList
|
||||||
|
import org.isoron.uhabits.core.models.HabitList.Order
|
||||||
|
import org.isoron.uhabits.core.models.HabitMatcher
|
||||||
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
||||||
|
import java.util.LinkedList
|
||||||
|
import java.util.Objects
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In-memory implementation of [HabitGroupList].
|
||||||
|
*/
|
||||||
|
class MemoryHabitGroupList : HabitGroupList {
|
||||||
|
private val list = LinkedList<HabitGroup>()
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
override var primaryOrder = Order.BY_POSITION
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
comparator = getComposedComparatorByOrder(primaryOrder, secondaryOrder)
|
||||||
|
resort()
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
override var secondaryOrder = Order.BY_NAME_ASC
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
comparator = getComposedComparatorByOrder(primaryOrder, secondaryOrder)
|
||||||
|
resort()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var comparator: Comparator<HabitGroup>? =
|
||||||
|
getComposedComparatorByOrder(primaryOrder, secondaryOrder)
|
||||||
|
private var parent: MemoryHabitGroupList? = null
|
||||||
|
|
||||||
|
constructor() : super()
|
||||||
|
constructor(
|
||||||
|
matcher: HabitMatcher,
|
||||||
|
comparator: Comparator<HabitGroup>?,
|
||||||
|
parent: MemoryHabitGroupList
|
||||||
|
) : super(matcher) {
|
||||||
|
this.parent = parent
|
||||||
|
this.comparator = comparator
|
||||||
|
primaryOrder = parent.primaryOrder
|
||||||
|
secondaryOrder = parent.secondaryOrder
|
||||||
|
parent.observable.addListener { loadFromParent() }
|
||||||
|
loadFromParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
@Throws(IllegalArgumentException::class)
|
||||||
|
override fun add(habitGroup: HabitGroup) {
|
||||||
|
throwIfHasParent()
|
||||||
|
require(!list.contains(habitGroup)) { "habit already added" }
|
||||||
|
val id = habitGroup.id
|
||||||
|
if (id != null && getById(id) != null) throw RuntimeException("duplicate id")
|
||||||
|
if (id == null) habitGroup.id = list.size.toLong()
|
||||||
|
list.addLast(habitGroup)
|
||||||
|
resort()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getById(id: Long): HabitGroup? {
|
||||||
|
for (h in list) {
|
||||||
|
checkNotNull(h.id)
|
||||||
|
if (h.id == id) return h
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getByUUID(uuid: String?): HabitGroup? {
|
||||||
|
for (h in list) if (Objects.requireNonNull(h.uuid) == uuid) return h
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getByPosition(position: Int): HabitGroup {
|
||||||
|
return list[position]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getFiltered(matcher: HabitMatcher?): HabitGroupList {
|
||||||
|
return MemoryHabitGroupList(matcher!!, comparator, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getComposedComparatorByOrder(
|
||||||
|
firstOrder: Order,
|
||||||
|
secondOrder: Order?
|
||||||
|
): Comparator<HabitGroup> {
|
||||||
|
return Comparator { h1: HabitGroup, h2: HabitGroup ->
|
||||||
|
val firstResult = getComparatorByOrder(firstOrder).compare(h1, h2)
|
||||||
|
if (firstResult != 0 || secondOrder == null) {
|
||||||
|
return@Comparator firstResult
|
||||||
|
}
|
||||||
|
getComparatorByOrder(secondOrder).compare(h1, h2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getComparatorByOrder(order: Order): Comparator<HabitGroup> {
|
||||||
|
val nameComparatorAsc = Comparator<HabitGroup> { habit1, habit2 ->
|
||||||
|
habit1.name.compareTo(habit2.name)
|
||||||
|
}
|
||||||
|
val nameComparatorDesc =
|
||||||
|
Comparator { h1: HabitGroup, h2: HabitGroup -> nameComparatorAsc.compare(h2, h1) }
|
||||||
|
val colorComparatorAsc = Comparator<HabitGroup> { (color1), (color2) ->
|
||||||
|
color1.compareTo(color2)
|
||||||
|
}
|
||||||
|
val colorComparatorDesc =
|
||||||
|
Comparator { h1: HabitGroup, h2: HabitGroup -> colorComparatorAsc.compare(h2, h1) }
|
||||||
|
val scoreComparatorDesc =
|
||||||
|
Comparator<HabitGroup> { habit1, habit2 ->
|
||||||
|
val today = getTodayWithOffset()
|
||||||
|
habit1.scores[today].value.compareTo(habit2.scores[today].value)
|
||||||
|
}
|
||||||
|
val scoreComparatorAsc =
|
||||||
|
Comparator { h1: HabitGroup, h2: HabitGroup -> scoreComparatorDesc.compare(h2, h1) }
|
||||||
|
val positionComparator =
|
||||||
|
Comparator<HabitGroup> { habit1, habit2 -> habit1.position.compareTo(habit2.position) }
|
||||||
|
val statusComparatorDesc = Comparator { h1: HabitGroup, h2: HabitGroup ->
|
||||||
|
if (h1.isCompletedToday() != h2.isCompletedToday()) {
|
||||||
|
return@Comparator if (h1.isCompletedToday()) -1 else 1
|
||||||
|
}
|
||||||
|
val today = getTodayWithOffset()
|
||||||
|
val v1 = h1.scores[today].value
|
||||||
|
val v2 = h2.scores[today].value
|
||||||
|
v2.compareTo(v1)
|
||||||
|
}
|
||||||
|
val statusComparatorAsc =
|
||||||
|
Comparator { h1: HabitGroup, h2: HabitGroup -> statusComparatorDesc.compare(h2, h1) }
|
||||||
|
return when {
|
||||||
|
order === Order.BY_POSITION -> positionComparator
|
||||||
|
order === Order.BY_NAME_ASC -> nameComparatorAsc
|
||||||
|
order === Order.BY_NAME_DESC -> nameComparatorDesc
|
||||||
|
order === Order.BY_COLOR_ASC -> colorComparatorAsc
|
||||||
|
order === Order.BY_COLOR_DESC -> colorComparatorDesc
|
||||||
|
order === Order.BY_SCORE_DESC -> scoreComparatorDesc
|
||||||
|
order === Order.BY_SCORE_ASC -> scoreComparatorAsc
|
||||||
|
order === Order.BY_STATUS_DESC -> statusComparatorDesc
|
||||||
|
order === Order.BY_STATUS_ASC -> statusComparatorAsc
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun indexOf(h: HabitGroup): Int {
|
||||||
|
return list.indexOf(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun iterator(): Iterator<HabitGroup> {
|
||||||
|
return ArrayList(list).iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun remove(h: HabitGroup) {
|
||||||
|
throwIfHasParent()
|
||||||
|
list.remove(h)
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun reorder(from: HabitGroup, to: HabitGroup) {
|
||||||
|
throwIfHasParent()
|
||||||
|
check(!(primaryOrder !== Order.BY_POSITION)) { "cannot reorder automatically sorted list" }
|
||||||
|
require(indexOf(from) >= 0) { "list does not contain (from) habit" }
|
||||||
|
val toPos = indexOf(to)
|
||||||
|
require(toPos >= 0) { "list does not contain (to) habit" }
|
||||||
|
list.remove(from)
|
||||||
|
list.add(toPos, from)
|
||||||
|
var position = 0
|
||||||
|
for (h in list) h.position = position++
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun size(): Int {
|
||||||
|
return list.size
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun update(habitGroups: List<HabitGroup>) {
|
||||||
|
resort()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun throwIfHasParent() {
|
||||||
|
check(parent == null) {
|
||||||
|
"Filtered lists cannot be modified directly. " +
|
||||||
|
"You should modify the parent list instead."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun loadFromParent() {
|
||||||
|
checkNotNull(parent)
|
||||||
|
list.clear()
|
||||||
|
for (h in parent!!) if (filter.matches(h)) list.add(h)
|
||||||
|
resort()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun resort() {
|
||||||
|
for (hgr in list) {
|
||||||
|
hgr.habitList.primaryOrder = primaryOrder
|
||||||
|
hgr.habitList.secondaryOrder = secondaryOrder
|
||||||
|
hgr.habitList.resort()
|
||||||
|
|
||||||
|
hgr.habitGroupList.primaryOrder = primaryOrder
|
||||||
|
hgr.habitGroupList.secondaryOrder = secondaryOrder
|
||||||
|
hgr.habitGroupList.resort()
|
||||||
|
}
|
||||||
|
if (comparator != null) list.sortWith(comparator!!)
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,8 +27,10 @@ class MemoryModelFactory : ModelFactory {
|
|||||||
override fun buildComputedEntries() = EntryList()
|
override fun buildComputedEntries() = EntryList()
|
||||||
override fun buildOriginalEntries() = EntryList()
|
override fun buildOriginalEntries() = EntryList()
|
||||||
override fun buildHabitList() = MemoryHabitList()
|
override fun buildHabitList() = MemoryHabitList()
|
||||||
|
override fun buildHabitGroupList() = MemoryHabitGroupList()
|
||||||
override fun buildScoreList() = ScoreList()
|
override fun buildScoreList() = ScoreList()
|
||||||
override fun buildStreakList() = StreakList()
|
override fun buildStreakList() = StreakList()
|
||||||
override fun buildHabitListRepository() = throw NotImplementedError()
|
override fun buildHabitListRepository() = throw NotImplementedError()
|
||||||
override fun buildRepetitionListRepository() = throw NotImplementedError()
|
override fun buildRepetitionListRepository() = throw NotImplementedError()
|
||||||
|
override fun buildHabitGroupListRepository() = throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.isoron.uhabits.core.models.ModelFactory
|
|||||||
import org.isoron.uhabits.core.models.ScoreList
|
import org.isoron.uhabits.core.models.ScoreList
|
||||||
import org.isoron.uhabits.core.models.StreakList
|
import org.isoron.uhabits.core.models.StreakList
|
||||||
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord
|
||||||
|
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
|
||||||
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
import org.isoron.uhabits.core.models.sqlite.records.HabitRecord
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -38,6 +39,8 @@ class SQLModelFactory
|
|||||||
override fun buildOriginalEntries() = SQLiteEntryList(database)
|
override fun buildOriginalEntries() = SQLiteEntryList(database)
|
||||||
override fun buildComputedEntries() = EntryList()
|
override fun buildComputedEntries() = EntryList()
|
||||||
override fun buildHabitList() = SQLiteHabitList(this)
|
override fun buildHabitList() = SQLiteHabitList(this)
|
||||||
|
override fun buildHabitGroupList() = SQLiteHabitGroupList(this)
|
||||||
|
|
||||||
override fun buildScoreList() = ScoreList()
|
override fun buildScoreList() = ScoreList()
|
||||||
override fun buildStreakList() = StreakList()
|
override fun buildStreakList() = StreakList()
|
||||||
|
|
||||||
@@ -46,4 +49,7 @@ class SQLModelFactory
|
|||||||
|
|
||||||
override fun buildRepetitionListRepository() =
|
override fun buildRepetitionListRepository() =
|
||||||
Repository(EntryRecord::class.java, database)
|
Repository(EntryRecord::class.java, database)
|
||||||
|
|
||||||
|
override fun buildHabitGroupListRepository() =
|
||||||
|
Repository(HabitGroupRecord::class.java, database)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package org.isoron.uhabits.core.models.sqlite
|
||||||
|
|
||||||
|
import org.isoron.uhabits.core.database.Repository
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroup
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroupList
|
||||||
|
import org.isoron.uhabits.core.models.HabitList.Order
|
||||||
|
import org.isoron.uhabits.core.models.HabitMatcher
|
||||||
|
import org.isoron.uhabits.core.models.ModelFactory
|
||||||
|
import org.isoron.uhabits.core.models.memory.MemoryHabitGroupList
|
||||||
|
import org.isoron.uhabits.core.models.sqlite.records.HabitGroupRecord
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of a [HabitGroupList] that is backed by SQLite.
|
||||||
|
*/
|
||||||
|
class SQLiteHabitGroupList @Inject constructor(private val modelFactory: ModelFactory) : HabitGroupList() {
|
||||||
|
private val repository: Repository<HabitGroupRecord> = modelFactory.buildHabitGroupListRepository()
|
||||||
|
private val list: MemoryHabitGroupList = MemoryHabitGroupList()
|
||||||
|
private var loaded = false
|
||||||
|
private fun loadRecords() {
|
||||||
|
if (loaded) return
|
||||||
|
loaded = true
|
||||||
|
list.removeAll()
|
||||||
|
val records = repository.findAll("order by position")
|
||||||
|
var shouldRebuildOrder = false
|
||||||
|
for ((expectedPosition, rec) in records.withIndex()) {
|
||||||
|
if (rec.position != expectedPosition) shouldRebuildOrder = true
|
||||||
|
val h = modelFactory.buildHabitGroup()
|
||||||
|
rec.copyTo(h)
|
||||||
|
list.add(h)
|
||||||
|
}
|
||||||
|
if (shouldRebuildOrder) rebuildOrder()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun add(habitGroup: HabitGroup) {
|
||||||
|
loadRecords()
|
||||||
|
habitGroup.position = size()
|
||||||
|
val record = HabitGroupRecord()
|
||||||
|
record.copyFrom(habitGroup)
|
||||||
|
repository.save(record)
|
||||||
|
habitGroup.id = record.id
|
||||||
|
list.add(habitGroup)
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getById(id: Long): HabitGroup? {
|
||||||
|
loadRecords()
|
||||||
|
return list.getById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getByUUID(uuid: String?): HabitGroup? {
|
||||||
|
loadRecords()
|
||||||
|
return list.getByUUID(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getByPosition(position: Int): HabitGroup {
|
||||||
|
loadRecords()
|
||||||
|
return list.getByPosition(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun getFiltered(matcher: HabitMatcher?): HabitGroupList {
|
||||||
|
loadRecords()
|
||||||
|
return list.getFiltered(matcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
@set:Synchronized
|
||||||
|
override var primaryOrder: Order
|
||||||
|
get() = list.primaryOrder
|
||||||
|
set(order) {
|
||||||
|
list.primaryOrder = order
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@set:Synchronized
|
||||||
|
override var secondaryOrder: Order
|
||||||
|
get() = list.secondaryOrder
|
||||||
|
set(order) {
|
||||||
|
list.secondaryOrder = order
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun indexOf(h: HabitGroup): Int {
|
||||||
|
loadRecords()
|
||||||
|
return list.indexOf(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun iterator(): Iterator<HabitGroup> {
|
||||||
|
loadRecords()
|
||||||
|
return list.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun rebuildOrder() {
|
||||||
|
val records = repository.findAll("order by position")
|
||||||
|
repository.executeAsTransaction {
|
||||||
|
for ((pos, r) in records.withIndex()) {
|
||||||
|
if (r.position != pos) {
|
||||||
|
r.position = pos
|
||||||
|
repository.save(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun remove(h: HabitGroup) {
|
||||||
|
loadRecords()
|
||||||
|
list.remove(h)
|
||||||
|
val record = repository.find(
|
||||||
|
h.id!!
|
||||||
|
) ?: throw RuntimeException("habit not in database")
|
||||||
|
repository.executeAsTransaction {
|
||||||
|
repository.remove(record)
|
||||||
|
}
|
||||||
|
rebuildOrder()
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun removeAll() {
|
||||||
|
list.removeAll()
|
||||||
|
repository.execSQL("delete from habits")
|
||||||
|
repository.execSQL("delete from repetitions")
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun reorder(from: HabitGroup, to: HabitGroup) {
|
||||||
|
loadRecords()
|
||||||
|
list.reorder(from, to)
|
||||||
|
val fromRecord = repository.find(
|
||||||
|
from.id!!
|
||||||
|
)
|
||||||
|
val toRecord = repository.find(
|
||||||
|
to.id!!
|
||||||
|
)
|
||||||
|
if (fromRecord == null) throw RuntimeException("habit not in database")
|
||||||
|
if (toRecord == null) throw RuntimeException("habit not in database")
|
||||||
|
if (toRecord.position!! < fromRecord.position!!) {
|
||||||
|
repository.execSQL(
|
||||||
|
"update habits set position = position + 1 " +
|
||||||
|
"where position >= ? and position < ?",
|
||||||
|
toRecord.position!!,
|
||||||
|
fromRecord.position!!
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
repository.execSQL(
|
||||||
|
"update habits set position = position - 1 " +
|
||||||
|
"where position > ? and position <= ?",
|
||||||
|
fromRecord.position!!,
|
||||||
|
toRecord.position!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fromRecord.position = toRecord.position
|
||||||
|
repository.save(fromRecord)
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun repair() {
|
||||||
|
loadRecords()
|
||||||
|
rebuildOrder()
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun size(): Int {
|
||||||
|
loadRecords()
|
||||||
|
return list.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
override fun update(habitGroups: List<HabitGroup>) {
|
||||||
|
loadRecords()
|
||||||
|
list.update(habitGroups)
|
||||||
|
for (h in habitGroups) {
|
||||||
|
val record = repository.find(h.id!!) ?: continue
|
||||||
|
record.copyFrom(h)
|
||||||
|
repository.save(record)
|
||||||
|
}
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resort() {
|
||||||
|
list.resort()
|
||||||
|
observable.notifyListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun reload() {
|
||||||
|
loaded = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package org.isoron.uhabits.core.models.sqlite.records
|
||||||
|
|
||||||
|
import org.isoron.uhabits.core.database.Column
|
||||||
|
import org.isoron.uhabits.core.database.Table
|
||||||
|
import org.isoron.uhabits.core.models.HabitGroup
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
|
import org.isoron.uhabits.core.models.WeekdayList
|
||||||
|
import java.util.Objects.requireNonNull
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SQLite database record corresponding to a [HabitGroup].
|
||||||
|
*/
|
||||||
|
@Table(name = "habitgroups")
|
||||||
|
class HabitGroupRecord {
|
||||||
|
@field:Column
|
||||||
|
var description: String? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var question: String? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var name: String? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var color: Int? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var position: Int? = null
|
||||||
|
|
||||||
|
@field:Column(name = "reminder_hour")
|
||||||
|
var reminderHour: Int? = null
|
||||||
|
|
||||||
|
@field:Column(name = "reminder_min")
|
||||||
|
var reminderMin: Int? = null
|
||||||
|
|
||||||
|
@field:Column(name = "reminder_days")
|
||||||
|
var reminderDays: Int? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var highlight: Int? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var archived: Int? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
@field:Column
|
||||||
|
var uuid: String? = null
|
||||||
|
|
||||||
|
@field:Column(name = "parent_id")
|
||||||
|
var parentID: Long? = null
|
||||||
|
|
||||||
|
@field:Column(name = "parent_uuid")
|
||||||
|
var parentUUID: String? = null
|
||||||
|
|
||||||
|
fun copyFrom(model: HabitGroup) {
|
||||||
|
id = model.id
|
||||||
|
name = model.name
|
||||||
|
description = model.description
|
||||||
|
highlight = 0
|
||||||
|
color = model.color.paletteIndex
|
||||||
|
archived = if (model.isArchived) 1 else 0
|
||||||
|
position = model.position
|
||||||
|
question = model.question
|
||||||
|
uuid = model.uuid
|
||||||
|
reminderDays = 0
|
||||||
|
reminderMin = null
|
||||||
|
reminderHour = null
|
||||||
|
parentID = model.parentID
|
||||||
|
parentUUID = model.parentUUID
|
||||||
|
if (model.hasReminder()) {
|
||||||
|
val reminder = model.reminder
|
||||||
|
reminderHour = requireNonNull(reminder)!!.hour
|
||||||
|
reminderMin = reminder!!.minute
|
||||||
|
reminderDays = reminder.days.toInteger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyTo(habitGroup: HabitGroup) {
|
||||||
|
habitGroup.id = id
|
||||||
|
habitGroup.name = name!!
|
||||||
|
habitGroup.description = description!!
|
||||||
|
habitGroup.question = question!!
|
||||||
|
habitGroup.color = PaletteColor(color!!)
|
||||||
|
habitGroup.isArchived = archived != 0
|
||||||
|
habitGroup.position = position!!
|
||||||
|
habitGroup.uuid = uuid
|
||||||
|
habitGroup.parentID = parentID
|
||||||
|
habitGroup.parentUUID = parentUUID
|
||||||
|
if (reminderHour != null && reminderMin != null) {
|
||||||
|
habitGroup.reminder = Reminder(
|
||||||
|
reminderHour!!,
|
||||||
|
reminderMin!!,
|
||||||
|
WeekdayList(reminderDays!!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,6 +95,12 @@ class HabitRecord {
|
|||||||
@field:Column
|
@field:Column
|
||||||
var uuid: String? = null
|
var uuid: String? = null
|
||||||
|
|
||||||
|
@field:Column(name = "parent_id")
|
||||||
|
var parentID: Long? = null
|
||||||
|
|
||||||
|
@field:Column(name = "parent_uuid")
|
||||||
|
var parentUUID: String? = null
|
||||||
|
|
||||||
fun copyFrom(model: Habit) {
|
fun copyFrom(model: Habit) {
|
||||||
id = model.id
|
id = model.id
|
||||||
name = model.name
|
name = model.name
|
||||||
@@ -109,6 +115,8 @@ class HabitRecord {
|
|||||||
position = model.position
|
position = model.position
|
||||||
question = model.question
|
question = model.question
|
||||||
uuid = model.uuid
|
uuid = model.uuid
|
||||||
|
parentID = model.parentID
|
||||||
|
parentUUID = model.parentUUID
|
||||||
val (numerator, denominator) = model.frequency
|
val (numerator, denominator) = model.frequency
|
||||||
freqNum = numerator
|
freqNum = numerator
|
||||||
freqDen = denominator
|
freqDen = denominator
|
||||||
@@ -140,6 +148,8 @@ class HabitRecord {
|
|||||||
habit.unit = unit!!
|
habit.unit = unit!!
|
||||||
habit.position = position!!
|
habit.position = position!!
|
||||||
habit.uuid = uuid
|
habit.uuid = uuid
|
||||||
|
habit.parentID = parentID
|
||||||
|
habit.parentUUID = parentUUID
|
||||||
if (reminderHour != null && reminderMin != null) {
|
if (reminderHour != null && reminderMin != null) {
|
||||||
habit.reminder = Reminder(
|
habit.reminder = Reminder(
|
||||||
reminderHour!!,
|
reminderHour!!,
|
||||||
|
|||||||
@@ -1,2 +1,21 @@
|
|||||||
alter table Habits add column skip_days integer not null default 0;
|
alter table Habits add column skip_days integer not null default 0;
|
||||||
alter table Habits add column skip_days_list integer not null default 0;
|
alter table Habits add column skip_days_list integer not null default 0;
|
||||||
|
alter table Habits add column parent_id integer;
|
||||||
|
alter table Habits add column parent_uuid text;
|
||||||
|
|
||||||
|
create table HabitGroups (
|
||||||
|
id integer primary key autoincrement,
|
||||||
|
archived integer,
|
||||||
|
color integer,
|
||||||
|
description text not null default "",
|
||||||
|
highlight integer,
|
||||||
|
name text,
|
||||||
|
position integer,
|
||||||
|
reminder_days integer not null default 127,
|
||||||
|
reminder_hour integer,
|
||||||
|
reminder_min integer,
|
||||||
|
question text not null default "",
|
||||||
|
uuid text,
|
||||||
|
parent_id integer,
|
||||||
|
parent_uuid integer
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user