|
|
@ -25,15 +25,15 @@ import org.isoron.uhabits.core.commands.CommandRunner
|
|
|
|
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
|
|
|
import org.isoron.uhabits.core.commands.CreateRepetitionCommand
|
|
|
|
import org.isoron.uhabits.core.io.Logging
|
|
|
|
import org.isoron.uhabits.core.io.Logging
|
|
|
|
import org.isoron.uhabits.core.models.Habit
|
|
|
|
import org.isoron.uhabits.core.models.Habit
|
|
|
|
|
|
|
|
import org.isoron.uhabits.core.models.HabitGroup
|
|
|
|
|
|
|
|
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.HabitList.Order
|
|
|
|
import org.isoron.uhabits.core.models.HabitList.Order
|
|
|
|
import org.isoron.uhabits.core.models.HabitMatcher
|
|
|
|
import org.isoron.uhabits.core.models.HabitMatcher
|
|
|
|
import org.isoron.uhabits.core.tasks.Task
|
|
|
|
import org.isoron.uhabits.core.tasks.Task
|
|
|
|
import org.isoron.uhabits.core.tasks.TaskRunner
|
|
|
|
import org.isoron.uhabits.core.tasks.TaskRunner
|
|
|
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
|
|
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
|
|
|
import java.util.ArrayList
|
|
|
|
|
|
|
|
import java.util.Arrays
|
|
|
|
import java.util.Arrays
|
|
|
|
import java.util.HashMap
|
|
|
|
|
|
|
|
import java.util.LinkedList
|
|
|
|
import java.util.LinkedList
|
|
|
|
import java.util.TreeSet
|
|
|
|
import java.util.TreeSet
|
|
|
|
import javax.inject.Inject
|
|
|
|
import javax.inject.Inject
|
|
|
@ -54,6 +54,7 @@ import javax.inject.Inject
|
|
|
|
@AppScope
|
|
|
|
@AppScope
|
|
|
|
class HabitCardListCache @Inject constructor(
|
|
|
|
class HabitCardListCache @Inject constructor(
|
|
|
|
private val allHabits: HabitList,
|
|
|
|
private val allHabits: HabitList,
|
|
|
|
|
|
|
|
private val allHabitGroups: HabitGroupList,
|
|
|
|
private val commandRunner: CommandRunner,
|
|
|
|
private val commandRunner: CommandRunner,
|
|
|
|
taskRunner: TaskRunner,
|
|
|
|
taskRunner: TaskRunner,
|
|
|
|
logging: Logging
|
|
|
|
logging: Logging
|
|
|
@ -66,6 +67,7 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
private var listener: Listener
|
|
|
|
private var listener: Listener
|
|
|
|
private val data: CacheData
|
|
|
|
private val data: CacheData
|
|
|
|
private var filteredHabits: HabitList
|
|
|
|
private var filteredHabits: HabitList
|
|
|
|
|
|
|
|
private var filteredHabitGroups: HabitGroupList
|
|
|
|
private val taskRunner: TaskRunner
|
|
|
|
private val taskRunner: TaskRunner
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -74,13 +76,13 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun getCheckmarks(habitId: Long): IntArray {
|
|
|
|
fun getCheckmarks(habitUUID: String): IntArray {
|
|
|
|
return data.checkmarks[habitId]!!
|
|
|
|
return data.checkmarks[habitUUID]!!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun getNotes(habitId: Long): Array<String> {
|
|
|
|
fun getNotes(habitUUID: String): Array<String> {
|
|
|
|
return data.notes[habitId]!!
|
|
|
|
return data.notes[habitUUID]!!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -88,21 +90,53 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
return allHabits.isEmpty
|
|
|
|
return allHabits.isEmpty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
|
|
|
|
fun hasNoHabitGroup(): Boolean {
|
|
|
|
|
|
|
|
return allHabitGroups.isEmpty
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns the habits that occupies a certain position on the list.
|
|
|
|
* Returns the habits that occupies a certain position on the list.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param position the position of the habit
|
|
|
|
* @param position the position of the list of habits and groups
|
|
|
|
* @return the habit at given position or null if position is invalid
|
|
|
|
* @return the habit at given position or null if position is invalid
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun getHabitByPosition(position: Int): Habit? {
|
|
|
|
fun getHabitByPosition(position: Int): Habit? {
|
|
|
|
return if (position < 0 || position >= data.habits.size) null else data.habits[position]
|
|
|
|
return if (position < 0 || position >= data.habits.size) {
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
data.habits[position]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Returns the habit groups that occupies a certain position on the list.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param position the position of the list of habits and groups
|
|
|
|
|
|
|
|
* @return the habit group at given position or null if position is invalid
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
|
|
|
|
fun getHabitGroupByPosition(position: Int): HabitGroup? {
|
|
|
|
|
|
|
|
return if (position < data.habits.size || position >= data.habits.size + data.habitGroups.size) {
|
|
|
|
|
|
|
|
null
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
data.habitGroups[position - data.habits.size]
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@get:Synchronized
|
|
|
|
|
|
|
|
val itemCount: Int
|
|
|
|
|
|
|
|
get() = habitCount + habitGroupCount
|
|
|
|
|
|
|
|
|
|
|
|
@get:Synchronized
|
|
|
|
@get:Synchronized
|
|
|
|
val habitCount: Int
|
|
|
|
val habitCount: Int
|
|
|
|
get() = data.habits.size
|
|
|
|
get() = data.habits.size
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@get:Synchronized
|
|
|
|
|
|
|
|
val habitGroupCount: Int
|
|
|
|
|
|
|
|
get() = data.habitGroups.size
|
|
|
|
|
|
|
|
|
|
|
|
@get:Synchronized
|
|
|
|
@get:Synchronized
|
|
|
|
@set:Synchronized
|
|
|
|
@set:Synchronized
|
|
|
|
var primaryOrder: Order
|
|
|
|
var primaryOrder: Order
|
|
|
@ -110,6 +144,8 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
set(order) {
|
|
|
|
set(order) {
|
|
|
|
allHabits.primaryOrder = order
|
|
|
|
allHabits.primaryOrder = order
|
|
|
|
filteredHabits.primaryOrder = order
|
|
|
|
filteredHabits.primaryOrder = order
|
|
|
|
|
|
|
|
allHabitGroups.primaryOrder = order
|
|
|
|
|
|
|
|
filteredHabitGroups.primaryOrder = order
|
|
|
|
refreshAllHabits()
|
|
|
|
refreshAllHabits()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -120,12 +156,14 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
set(order) {
|
|
|
|
set(order) {
|
|
|
|
allHabits.secondaryOrder = order
|
|
|
|
allHabits.secondaryOrder = order
|
|
|
|
filteredHabits.secondaryOrder = order
|
|
|
|
filteredHabits.secondaryOrder = order
|
|
|
|
|
|
|
|
allHabitGroups.secondaryOrder = order
|
|
|
|
|
|
|
|
filteredHabitGroups.secondaryOrder = order
|
|
|
|
refreshAllHabits()
|
|
|
|
refreshAllHabits()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun getScore(habitId: Long): Double {
|
|
|
|
fun getScore(habitUUID: String): Double {
|
|
|
|
return data.scores[habitId]!!
|
|
|
|
return data.scores[habitUUID]!!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -137,7 +175,7 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
override fun onCommandFinished(command: Command) {
|
|
|
|
override fun onCommandFinished(command: Command) {
|
|
|
|
if (command is CreateRepetitionCommand) {
|
|
|
|
if (command is CreateRepetitionCommand) {
|
|
|
|
command.habit.id?.let { refreshHabit(it) }
|
|
|
|
command.habit.uuid?.let { refreshHabit(it) }
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
refreshAllHabits()
|
|
|
|
refreshAllHabits()
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -157,27 +195,47 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun refreshHabit(id: Long) {
|
|
|
|
fun refreshHabit(uuid: String) {
|
|
|
|
taskRunner.execute(RefreshTask(id))
|
|
|
|
taskRunner.execute(RefreshTask(uuid))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun remove(id: Long) {
|
|
|
|
fun remove(uuid: String) {
|
|
|
|
val h = data.idToHabit[id] ?: return
|
|
|
|
val h = data.uuidToHabit[uuid]
|
|
|
|
val position = data.habits.indexOf(h)
|
|
|
|
if (h != null) {
|
|
|
|
data.habits.removeAt(position)
|
|
|
|
val position = data.habits.indexOf(h)
|
|
|
|
data.idToHabit.remove(id)
|
|
|
|
data.habits.removeAt(position)
|
|
|
|
data.checkmarks.remove(id)
|
|
|
|
data.uuidToHabit.remove(uuid)
|
|
|
|
data.notes.remove(id)
|
|
|
|
data.checkmarks.remove(uuid)
|
|
|
|
data.scores.remove(id)
|
|
|
|
data.notes.remove(uuid)
|
|
|
|
listener.onItemRemoved(position)
|
|
|
|
data.scores.remove(uuid)
|
|
|
|
|
|
|
|
listener.onItemRemoved(position)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
val hgr = data.uuidToHabitGroup[uuid]
|
|
|
|
|
|
|
|
if (hgr != null) {
|
|
|
|
|
|
|
|
val position = data.habitGroups.indexOf(hgr)
|
|
|
|
|
|
|
|
data.habitGroups.removeAt(position)
|
|
|
|
|
|
|
|
data.uuidToHabitGroup.remove(uuid)
|
|
|
|
|
|
|
|
listener.onItemRemoved(position + data.habits.size)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun reorder(from: Int, to: Int) {
|
|
|
|
fun reorder(from: Int, to: Int) {
|
|
|
|
val fromHabit = data.habits[from]
|
|
|
|
if (data.habits.size in (from + 1)..to || data.habits.size in (to + 1)..from) {
|
|
|
|
data.habits.removeAt(from)
|
|
|
|
logger.error("reorder: from and to are in different sections")
|
|
|
|
data.habits.add(to, fromHabit)
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (from < data.habits.size) {
|
|
|
|
|
|
|
|
val fromHabit = data.habits[from]
|
|
|
|
|
|
|
|
data.habits.removeAt(from)
|
|
|
|
|
|
|
|
data.habits.add(to, fromHabit)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
val fromHabitGroup = data.habitGroups[from]
|
|
|
|
|
|
|
|
data.habitGroups.removeAt(from - data.habits.size)
|
|
|
|
|
|
|
|
data.habitGroups.add(to - data.habits.size, fromHabitGroup)
|
|
|
|
|
|
|
|
}
|
|
|
|
listener.onItemMoved(from, to)
|
|
|
|
listener.onItemMoved(from, to)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -189,6 +247,7 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun setFilter(matcher: HabitMatcher) {
|
|
|
|
fun setFilter(matcher: HabitMatcher) {
|
|
|
|
filteredHabits = allHabits.getFiltered(matcher)
|
|
|
|
filteredHabits = allHabits.getFiltered(matcher)
|
|
|
|
|
|
|
|
filteredHabitGroups = allHabitGroups.getFiltered(matcher)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -209,21 +268,23 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private inner class CacheData {
|
|
|
|
private inner class CacheData {
|
|
|
|
val idToHabit: HashMap<Long?, Habit> = HashMap()
|
|
|
|
val uuidToHabit: HashMap<String?, Habit> = HashMap()
|
|
|
|
|
|
|
|
val uuidToHabitGroup: HashMap<String?, HabitGroup> = HashMap()
|
|
|
|
val habits: MutableList<Habit>
|
|
|
|
val habits: MutableList<Habit>
|
|
|
|
val checkmarks: HashMap<Long?, IntArray>
|
|
|
|
val habitGroups: MutableList<HabitGroup>
|
|
|
|
val scores: HashMap<Long?, Double>
|
|
|
|
val checkmarks: HashMap<String?, IntArray>
|
|
|
|
val notes: HashMap<Long?, Array<String>>
|
|
|
|
val scores: HashMap<String?, Double>
|
|
|
|
|
|
|
|
val notes: HashMap<String?, Array<String>>
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun copyCheckmarksFrom(oldData: CacheData) {
|
|
|
|
fun copyCheckmarksFrom(oldData: CacheData) {
|
|
|
|
val empty = IntArray(checkmarkCount)
|
|
|
|
val empty = IntArray(checkmarkCount)
|
|
|
|
for (id in idToHabit.keys) {
|
|
|
|
for (uuid in uuidToHabit.keys) {
|
|
|
|
if (oldData.checkmarks.containsKey(id)) {
|
|
|
|
if (oldData.checkmarks.containsKey(uuid)) {
|
|
|
|
checkmarks[id] =
|
|
|
|
checkmarks[uuid] =
|
|
|
|
oldData.checkmarks[id]!!
|
|
|
|
oldData.checkmarks[uuid]!!
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
checkmarks[id] = empty
|
|
|
|
checkmarks[uuid] = empty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -231,24 +292,32 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun copyNoteIndicatorsFrom(oldData: CacheData) {
|
|
|
|
fun copyNoteIndicatorsFrom(oldData: CacheData) {
|
|
|
|
val empty = (0..checkmarkCount).map { "" }.toTypedArray()
|
|
|
|
val empty = (0..checkmarkCount).map { "" }.toTypedArray()
|
|
|
|
for (id in idToHabit.keys) {
|
|
|
|
for (uuid in uuidToHabit.keys) {
|
|
|
|
if (oldData.notes.containsKey(id)) {
|
|
|
|
if (oldData.notes.containsKey(uuid)) {
|
|
|
|
notes[id] =
|
|
|
|
notes[uuid] =
|
|
|
|
oldData.notes[id]!!
|
|
|
|
oldData.notes[uuid]!!
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
notes[id] = empty
|
|
|
|
notes[uuid] = empty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun copyScoresFrom(oldData: CacheData) {
|
|
|
|
fun copyScoresFrom(oldData: CacheData) {
|
|
|
|
for (id in idToHabit.keys) {
|
|
|
|
for (uuid in uuidToHabit.keys) {
|
|
|
|
if (oldData.scores.containsKey(id)) {
|
|
|
|
if (oldData.scores.containsKey(uuid)) {
|
|
|
|
scores[id] =
|
|
|
|
scores[uuid] =
|
|
|
|
oldData.scores[id]!!
|
|
|
|
oldData.scores[uuid]!!
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
scores[id] = 0.0
|
|
|
|
scores[uuid] = 0.0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uuid in uuidToHabitGroup.keys) {
|
|
|
|
|
|
|
|
if (oldData.scores.containsKey(uuid)) {
|
|
|
|
|
|
|
|
scores[uuid] =
|
|
|
|
|
|
|
|
oldData.scores[uuid]!!
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
scores[uuid] = 0.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -256,9 +325,15 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
fun fetchHabits() {
|
|
|
|
fun fetchHabits() {
|
|
|
|
for (h in filteredHabits) {
|
|
|
|
for (h in filteredHabits) {
|
|
|
|
if (h.id == null) continue
|
|
|
|
if (h.uuid == null) continue
|
|
|
|
habits.add(h)
|
|
|
|
habits.add(h)
|
|
|
|
idToHabit[h.id] = h
|
|
|
|
uuidToHabit[h.uuid] = h
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (hgr in filteredHabitGroups) {
|
|
|
|
|
|
|
|
if (hgr.uuid == null) continue
|
|
|
|
|
|
|
|
habitGroups.add(hgr)
|
|
|
|
|
|
|
|
uuidToHabitGroup[hgr.uuid] = hgr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -267,6 +342,7 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
init {
|
|
|
|
init {
|
|
|
|
habits = LinkedList()
|
|
|
|
habits = LinkedList()
|
|
|
|
|
|
|
|
habitGroups = LinkedList()
|
|
|
|
checkmarks = HashMap()
|
|
|
|
checkmarks = HashMap()
|
|
|
|
scores = HashMap()
|
|
|
|
scores = HashMap()
|
|
|
|
notes = HashMap()
|
|
|
|
notes = HashMap()
|
|
|
@ -275,19 +351,19 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
|
|
|
|
|
|
|
|
private inner class RefreshTask : Task {
|
|
|
|
private inner class RefreshTask : Task {
|
|
|
|
private val newData: CacheData
|
|
|
|
private val newData: CacheData
|
|
|
|
private val targetId: Long?
|
|
|
|
private val targetUUID: String?
|
|
|
|
private var isCancelled = false
|
|
|
|
private var isCancelled = false
|
|
|
|
private var runner: TaskRunner? = null
|
|
|
|
private var runner: TaskRunner? = null
|
|
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
constructor() {
|
|
|
|
newData = CacheData()
|
|
|
|
newData = CacheData()
|
|
|
|
targetId = null
|
|
|
|
targetUUID = null
|
|
|
|
isCancelled = false
|
|
|
|
isCancelled = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
constructor(targetId: Long) {
|
|
|
|
constructor(targetUUID: String) {
|
|
|
|
newData = CacheData()
|
|
|
|
newData = CacheData()
|
|
|
|
this.targetId = targetId
|
|
|
|
this.targetUUID = targetUUID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -307,8 +383,8 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
for (position in newData.habits.indices) {
|
|
|
|
for (position in newData.habits.indices) {
|
|
|
|
if (isCancelled) return
|
|
|
|
if (isCancelled) return
|
|
|
|
val habit = newData.habits[position]
|
|
|
|
val habit = newData.habits[position]
|
|
|
|
if (targetId != null && targetId != habit.id) continue
|
|
|
|
if (targetUUID != null && targetUUID != habit.uuid) continue
|
|
|
|
newData.scores[habit.id] = habit.scores[today].value
|
|
|
|
newData.scores[habit.uuid] = habit.scores[today].value
|
|
|
|
val list: MutableList<Int> = ArrayList()
|
|
|
|
val list: MutableList<Int> = ArrayList()
|
|
|
|
val notes: MutableList<String> = ArrayList()
|
|
|
|
val notes: MutableList<String> = ArrayList()
|
|
|
|
for ((_, value, note) in habit.computedEntries.getByInterval(dateFrom, today)) {
|
|
|
|
for ((_, value, note) in habit.computedEntries.getByInterval(dateFrom, today)) {
|
|
|
@ -316,10 +392,18 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
notes.add(note)
|
|
|
|
notes.add(note)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val entries = list.toTypedArray()
|
|
|
|
val entries = list.toTypedArray()
|
|
|
|
newData.checkmarks[habit.id] = ArrayUtils.toPrimitive(entries)
|
|
|
|
newData.checkmarks[habit.uuid] = ArrayUtils.toPrimitive(entries)
|
|
|
|
newData.notes[habit.id] = notes.toTypedArray()
|
|
|
|
newData.notes[habit.uuid] = notes.toTypedArray()
|
|
|
|
runner!!.publishProgress(this, position)
|
|
|
|
runner!!.publishProgress(this, position)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (position in newData.habitGroups.indices) {
|
|
|
|
|
|
|
|
if (isCancelled) return
|
|
|
|
|
|
|
|
val hgr = newData.habitGroups[position]
|
|
|
|
|
|
|
|
if (targetUUID != null && targetUUID != hgr.uuid) continue
|
|
|
|
|
|
|
|
newData.scores[hgr.uuid] = hgr.scores[today].value
|
|
|
|
|
|
|
|
runner!!.publishProgress(this, position + newData.habits.size)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
@ -340,15 +424,29 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
private fun performInsert(habit: Habit, position: Int) {
|
|
|
|
private fun performInsert(habit: Habit, position: Int) {
|
|
|
|
val id = habit.id
|
|
|
|
val uuid = habit.uuid
|
|
|
|
data.habits.add(position, habit)
|
|
|
|
data.habits.add(position, habit)
|
|
|
|
data.idToHabit[id] = habit
|
|
|
|
data.uuidToHabit[uuid] = habit
|
|
|
|
data.scores[id] = newData.scores[id]!!
|
|
|
|
data.scores[uuid] = newData.scores[uuid]!!
|
|
|
|
data.checkmarks[id] = newData.checkmarks[id]!!
|
|
|
|
data.checkmarks[uuid] = newData.checkmarks[uuid]!!
|
|
|
|
data.notes[id] = newData.notes[id]!!
|
|
|
|
data.notes[uuid] = newData.notes[uuid]!!
|
|
|
|
listener.onItemInserted(position)
|
|
|
|
listener.onItemInserted(position)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
|
|
|
|
private fun performInsert(habitGroup: HabitGroup, position: Int) {
|
|
|
|
|
|
|
|
val newPosition = if (position < data.habits.size) {
|
|
|
|
|
|
|
|
data.habits.size
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
position
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
val uuid = habitGroup.uuid
|
|
|
|
|
|
|
|
data.habitGroups.add(newPosition - data.habits.size, habitGroup)
|
|
|
|
|
|
|
|
data.uuidToHabitGroup[uuid] = habitGroup
|
|
|
|
|
|
|
|
data.scores[uuid] = newData.scores[uuid]!!
|
|
|
|
|
|
|
|
listener.onItemInserted(newPosition)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
private fun performMove(
|
|
|
|
private fun performMove(
|
|
|
|
habit: Habit,
|
|
|
|
habit: Habit,
|
|
|
@ -359,7 +457,7 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
|
|
|
|
|
|
|
|
// Workaround for https://github.com/iSoron/uhabits/issues/968
|
|
|
|
// Workaround for https://github.com/iSoron/uhabits/issues/968
|
|
|
|
val checkedToPosition = if (toPosition > data.habits.size) {
|
|
|
|
val checkedToPosition = if (toPosition > data.habits.size) {
|
|
|
|
logger.error("performMove: $toPosition is strictly higher than ${data.habits.size}")
|
|
|
|
logger.error("performMove: $toPosition for habit is strictly higher than ${data.habits.size}")
|
|
|
|
data.habits.size
|
|
|
|
data.habits.size
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
toPosition
|
|
|
|
toPosition
|
|
|
@ -369,57 +467,114 @@ class HabitCardListCache @Inject constructor(
|
|
|
|
listener.onItemMoved(fromPosition, checkedToPosition)
|
|
|
|
listener.onItemMoved(fromPosition, checkedToPosition)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun performMove(
|
|
|
|
|
|
|
|
habitGroup: HabitGroup,
|
|
|
|
|
|
|
|
fromPosition: Int,
|
|
|
|
|
|
|
|
toPosition: Int
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
data.habitGroups.removeAt(fromPosition)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Workaround for https://github.com/iSoron/uhabits/issues/968
|
|
|
|
|
|
|
|
val checkedToPosition = if (toPosition < data.habits.size) {
|
|
|
|
|
|
|
|
logger.error("performMove: $toPosition for habit group is strictly lower than ${data.habits.size}")
|
|
|
|
|
|
|
|
data.habits.size
|
|
|
|
|
|
|
|
} else if (toPosition > data.habits.size + data.habitGroups.size) {
|
|
|
|
|
|
|
|
logger.error("performMove: $toPosition for habit group is strictly higher than ${data.habits.size + data.habitGroups.size}")
|
|
|
|
|
|
|
|
data.habits.size + data.habitGroups.size
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
toPosition
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data.habitGroups.add(checkedToPosition - data.habits.size, habitGroup)
|
|
|
|
|
|
|
|
listener.onItemMoved(fromPosition, checkedToPosition)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
private fun performUpdate(id: Long, position: Int) {
|
|
|
|
private fun performUpdate(uuid: String, position: Int) {
|
|
|
|
val oldScore = data.scores[id]!!
|
|
|
|
|
|
|
|
val oldCheckmarks = data.checkmarks[id]
|
|
|
|
|
|
|
|
val oldNoteIndicators = data.notes[id]
|
|
|
|
|
|
|
|
val newScore = newData.scores[id]!!
|
|
|
|
|
|
|
|
val newCheckmarks = newData.checkmarks[id]!!
|
|
|
|
|
|
|
|
val newNoteIndicators = newData.notes[id]!!
|
|
|
|
|
|
|
|
var unchanged = true
|
|
|
|
var unchanged = true
|
|
|
|
|
|
|
|
val oldScore = data.scores[uuid]!!
|
|
|
|
|
|
|
|
val newScore = newData.scores[uuid]!!
|
|
|
|
if (oldScore != newScore) unchanged = false
|
|
|
|
if (oldScore != newScore) unchanged = false
|
|
|
|
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false
|
|
|
|
|
|
|
|
if (!Arrays.equals(oldNoteIndicators, newNoteIndicators)) unchanged = false
|
|
|
|
if (position < data.habits.size) {
|
|
|
|
|
|
|
|
val oldCheckmarks = data.checkmarks[uuid]
|
|
|
|
|
|
|
|
val newCheckmarks = newData.checkmarks[uuid]!!
|
|
|
|
|
|
|
|
val oldNoteIndicators = data.notes[uuid]
|
|
|
|
|
|
|
|
val newNoteIndicators = newData.notes[uuid]!!
|
|
|
|
|
|
|
|
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false
|
|
|
|
|
|
|
|
if (!Arrays.equals(oldNoteIndicators, newNoteIndicators)) unchanged = false
|
|
|
|
|
|
|
|
if (unchanged) return
|
|
|
|
|
|
|
|
data.checkmarks[uuid] = newCheckmarks
|
|
|
|
|
|
|
|
data.notes[uuid] = newNoteIndicators
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (unchanged) return
|
|
|
|
if (unchanged) return
|
|
|
|
data.scores[id] = newScore
|
|
|
|
data.scores[uuid] = newScore
|
|
|
|
data.checkmarks[id] = newCheckmarks
|
|
|
|
|
|
|
|
data.notes[id] = newNoteIndicators
|
|
|
|
|
|
|
|
listener.onItemChanged(position)
|
|
|
|
listener.onItemChanged(position)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
private fun processPosition(currentPosition: Int) {
|
|
|
|
private fun processPosition(currentPosition: Int) {
|
|
|
|
val habit = newData.habits[currentPosition]
|
|
|
|
if (currentPosition < newData.habits.size) {
|
|
|
|
val id = habit.id
|
|
|
|
val habit = newData.habits[currentPosition]
|
|
|
|
val prevPosition = data.habits.indexOf(habit)
|
|
|
|
val uuid = habit.uuid
|
|
|
|
if (prevPosition < 0) {
|
|
|
|
val prevPosition = data.habits.indexOf(habit)
|
|
|
|
performInsert(habit, currentPosition)
|
|
|
|
if (prevPosition < 0) {
|
|
|
|
|
|
|
|
performInsert(habit, currentPosition)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (prevPosition != currentPosition) {
|
|
|
|
|
|
|
|
performMove(
|
|
|
|
|
|
|
|
habit,
|
|
|
|
|
|
|
|
prevPosition,
|
|
|
|
|
|
|
|
currentPosition
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uuid == null) throw NullPointerException()
|
|
|
|
|
|
|
|
performUpdate(uuid, currentPosition)
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (prevPosition != currentPosition) {
|
|
|
|
val habitGroup = newData.habitGroups[currentPosition - data.habits.size]
|
|
|
|
performMove(
|
|
|
|
val uuid = habitGroup.uuid
|
|
|
|
habit,
|
|
|
|
val prevPosition = data.habitGroups.indexOf(habitGroup) + data.habits.size
|
|
|
|
prevPosition,
|
|
|
|
if (prevPosition < 0) {
|
|
|
|
currentPosition
|
|
|
|
performInsert(habitGroup, currentPosition)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if (prevPosition != currentPosition) {
|
|
|
|
|
|
|
|
performMove(
|
|
|
|
|
|
|
|
habitGroup,
|
|
|
|
|
|
|
|
prevPosition,
|
|
|
|
|
|
|
|
currentPosition
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uuid == null) throw NullPointerException()
|
|
|
|
|
|
|
|
performUpdate(uuid, currentPosition)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (id == null) throw NullPointerException()
|
|
|
|
|
|
|
|
performUpdate(id, currentPosition)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
@Synchronized
|
|
|
|
private fun processRemovedHabits() {
|
|
|
|
private fun processRemovedHabits() {
|
|
|
|
val before: Set<Long?> = data.idToHabit.keys
|
|
|
|
val before: Set<String?> = data.uuidToHabit.keys
|
|
|
|
val after: Set<Long?> = newData.idToHabit.keys
|
|
|
|
val after: Set<String?> = newData.uuidToHabit.keys
|
|
|
|
val removed: MutableSet<Long?> = TreeSet(before)
|
|
|
|
val removed: MutableSet<String?> = TreeSet(before)
|
|
|
|
|
|
|
|
removed.removeAll(after)
|
|
|
|
|
|
|
|
for (uuid in removed) remove(uuid!!)
|
|
|
|
|
|
|
|
processRemovedHabitGroups()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Synchronized
|
|
|
|
|
|
|
|
private fun processRemovedHabitGroups() {
|
|
|
|
|
|
|
|
val before: Set<String?> = data.uuidToHabitGroup.keys
|
|
|
|
|
|
|
|
val after: Set<String?> = newData.uuidToHabitGroup.keys
|
|
|
|
|
|
|
|
val removed: MutableSet<String?> = TreeSet(before)
|
|
|
|
removed.removeAll(after)
|
|
|
|
removed.removeAll(after)
|
|
|
|
for (id in removed) remove(id!!)
|
|
|
|
for (uuid in removed) remove(uuid!!)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
init {
|
|
|
|
init {
|
|
|
|
filteredHabits = allHabits
|
|
|
|
filteredHabits = allHabits
|
|
|
|
|
|
|
|
filteredHabitGroups = allHabitGroups
|
|
|
|
this.taskRunner = taskRunner
|
|
|
|
this.taskRunner = taskRunner
|
|
|
|
listener = object : Listener {}
|
|
|
|
listener = object : Listener {}
|
|
|
|
data = CacheData()
|
|
|
|
data = CacheData()
|
|
|
|