mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-14 21:18:51 -06:00
Remove SkipDays feature
This commit is contained in:
@@ -33,7 +33,6 @@ import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
|||||||
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
import org.isoron.uhabits.utils.formatTime
|
import org.isoron.uhabits.utils.formatTime
|
||||||
import org.isoron.uhabits.utils.toFormattedString
|
|
||||||
|
|
||||||
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
|
||||||
|
|
||||||
@@ -79,12 +78,6 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
if (state.question.isEmpty()) {
|
if (state.question.isEmpty()) {
|
||||||
binding.questionLabel.visibility = View.GONE
|
binding.questionLabel.visibility = View.GONE
|
||||||
}
|
}
|
||||||
if (state.skipDays.isSkipDays) {
|
|
||||||
binding.skipLabel.visibility = View.VISIBLE
|
|
||||||
binding.skipLabel.text = context.getString(R.string.skip_day) + " " + state.skipDays.days.toFormattedString(context)
|
|
||||||
} else {
|
|
||||||
binding.skipLabel.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
postInvalidate()
|
postInvalidate()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,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
|
||||||
@@ -104,6 +107,18 @@ data class Habit(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun firstEntryDate(): Timestamp {
|
||||||
|
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
|
||||||
@@ -119,6 +134,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 {
|
||||||
@@ -139,6 +156,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
|
||||||
}
|
}
|
||||||
@@ -158,6 +177,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
package org.isoron.uhabits.core.models
|
package org.isoron.uhabits.core.models
|
||||||
|
|
||||||
import org.isoron.uhabits.core.models.Score.Companion.compute
|
import org.isoron.uhabits.core.models.Score.Companion.compute
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.HashMap
|
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -138,4 +136,19 @@ class ScoreList {
|
|||||||
map[timestamp] = Score(timestamp, previousValue)
|
map[timestamp] = Score(timestamp, previousValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun combineFrom(
|
||||||
|
habitList: HabitList,
|
||||||
|
from: Timestamp,
|
||||||
|
to: Timestamp
|
||||||
|
) {
|
||||||
|
var current = to
|
||||||
|
while (current >= from) {
|
||||||
|
val habitScores = habitList.map { it.scores[current].value }
|
||||||
|
val averageScore = habitScores.average()
|
||||||
|
map[current] = Score(current, averageScore)
|
||||||
|
current = current.minus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package org.isoron.uhabits.core.models
|
|
||||||
|
|
||||||
data class SkipDays(
|
|
||||||
val isSkipDays: Boolean,
|
|
||||||
val days: WeekdayList
|
|
||||||
) {
|
|
||||||
fun isDaySkipped(day: Int): Boolean {
|
|
||||||
return isSkipDays && days.isDayTrue(day)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isDaySkipped(day: Timestamp): Boolean {
|
|
||||||
return isSkipDays && days.isDayTrue(day.weekday)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isDaySkipped(entry: Entry): Boolean {
|
|
||||||
return isSkipDays && days.isDayTrue(entry.timestamp.weekday)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmField
|
|
||||||
val NONE = SkipDays(false, WeekdayList(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -88,6 +88,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
|
||||||
@@ -102,6 +108,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
|
||||||
@@ -130,6 +138,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!!,
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ class HistoryCardPresenter(
|
|||||||
override fun onDateLongPress(date: LocalDate) {
|
override fun onDateLongPress(date: LocalDate) {
|
||||||
val timestamp = Timestamp.fromLocalDate(date)
|
val timestamp = Timestamp.fromLocalDate(date)
|
||||||
screen.showFeedback()
|
screen.showFeedback()
|
||||||
if (habit.skipDays.isDaySkipped(timestamp)) return
|
|
||||||
if (habit.isNumerical) {
|
if (habit.isNumerical) {
|
||||||
showNumberPopup(timestamp)
|
showNumberPopup(timestamp)
|
||||||
} else {
|
} else {
|
||||||
@@ -82,7 +81,6 @@ class HistoryCardPresenter(
|
|||||||
override fun onDateShortPress(date: LocalDate) {
|
override fun onDateShortPress(date: LocalDate) {
|
||||||
val timestamp = Timestamp.fromLocalDate(date)
|
val timestamp = Timestamp.fromLocalDate(date)
|
||||||
screen.showFeedback()
|
screen.showFeedback()
|
||||||
if (habit.skipDays.isDaySkipped(timestamp)) return
|
|
||||||
if (habit.isNumerical) {
|
if (habit.isNumerical) {
|
||||||
showNumberPopup(timestamp)
|
showNumberPopup(timestamp)
|
||||||
} else {
|
} else {
|
||||||
@@ -163,7 +161,7 @@ class HistoryCardPresenter(
|
|||||||
): HistoryCardState {
|
): HistoryCardState {
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
||||||
val entries = habit.computedEntries.getByInterval(oldest, today, habit.skipDays)
|
val entries = habit.computedEntries.getByInterval(oldest, today)
|
||||||
val series = if (habit.isNumerical) {
|
val series = if (habit.isNumerical) {
|
||||||
entries.map {
|
entries.map {
|
||||||
when {
|
when {
|
||||||
|
|||||||
@@ -24,14 +24,12 @@ import org.isoron.uhabits.core.models.Habit
|
|||||||
import org.isoron.uhabits.core.models.NumericalHabitType
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.models.Reminder
|
import org.isoron.uhabits.core.models.Reminder
|
||||||
import org.isoron.uhabits.core.models.SkipDays
|
|
||||||
import org.isoron.uhabits.core.ui.views.Theme
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
|
|
||||||
data class SubtitleCardState(
|
data class SubtitleCardState(
|
||||||
val color: PaletteColor,
|
val color: PaletteColor,
|
||||||
val frequency: Frequency,
|
val frequency: Frequency,
|
||||||
val isNumerical: Boolean,
|
val isNumerical: Boolean,
|
||||||
val skipDays: SkipDays,
|
|
||||||
val question: String,
|
val question: String,
|
||||||
val reminder: Reminder?,
|
val reminder: Reminder?,
|
||||||
val targetValue: Double = 0.0,
|
val targetValue: Double = 0.0,
|
||||||
@@ -49,7 +47,6 @@ class SubtitleCardPresenter {
|
|||||||
color = habit.color,
|
color = habit.color,
|
||||||
frequency = habit.frequency,
|
frequency = habit.frequency,
|
||||||
isNumerical = habit.isNumerical,
|
isNumerical = habit.isNumerical,
|
||||||
skipDays = habit.skipDays,
|
|
||||||
question = habit.question,
|
question = habit.question,
|
||||||
reminder = habit.reminder,
|
reminder = habit.reminder,
|
||||||
targetValue = habit.targetValue,
|
targetValue = habit.targetValue,
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.isoron.uhabits.core.models.countSkippedDays
|
|||||||
import org.isoron.uhabits.core.models.groupedSum
|
import org.isoron.uhabits.core.models.groupedSum
|
||||||
import org.isoron.uhabits.core.ui.views.Theme
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
@@ -46,61 +45,54 @@ class TargetCardPresenter {
|
|||||||
theme: Theme
|
theme: Theme
|
||||||
): TargetCardState {
|
): TargetCardState {
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
val (yearBegin, yearEnd) = getYearRange(firstWeekday)
|
|
||||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
||||||
val entriesForSkip = habit.computedEntries.getByInterval(yearBegin, yearEnd, habit.skipDays)
|
val entries = habit.computedEntries.getByInterval(oldest, today)
|
||||||
val entriesForSum = habit.computedEntries.getByInterval(oldest, today)
|
|
||||||
|
|
||||||
val valueToday = entriesForSum.groupedSum(
|
val valueToday = entries.groupedSum(
|
||||||
truncateField = DateUtils.TruncateField.DAY,
|
truncateField = DateUtils.TruncateField.DAY,
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val skippedDayToday = entriesForSkip.countSkippedDays(
|
val skippedDayToday = entries.countSkippedDays(
|
||||||
truncateField = DateUtils.TruncateField.DAY,
|
truncateField = DateUtils.TruncateField.DAY
|
||||||
skipDays = habit.skipDays
|
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val valueThisWeek = entriesForSum.groupedSum(
|
val valueThisWeek = entries.groupedSum(
|
||||||
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
||||||
firstWeekday = firstWeekday,
|
firstWeekday = firstWeekday,
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val skippedDaysThisWeek = entriesForSkip.countSkippedDays(
|
val skippedDaysThisWeek = entries.countSkippedDays(
|
||||||
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
truncateField = DateUtils.TruncateField.WEEK_NUMBER,
|
||||||
firstWeekday = firstWeekday,
|
firstWeekday = firstWeekday
|
||||||
skipDays = habit.skipDays
|
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val valueThisMonth = entriesForSum.groupedSum(
|
val valueThisMonth = entries.groupedSum(
|
||||||
truncateField = DateUtils.TruncateField.MONTH,
|
truncateField = DateUtils.TruncateField.MONTH,
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val skippedDaysThisMonth = entriesForSkip.countSkippedDays(
|
val skippedDaysThisMonth = entries.countSkippedDays(
|
||||||
truncateField = DateUtils.TruncateField.MONTH,
|
truncateField = DateUtils.TruncateField.MONTH
|
||||||
skipDays = habit.skipDays
|
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val valueThisQuarter = entriesForSum.groupedSum(
|
val valueThisQuarter = entries.groupedSum(
|
||||||
truncateField = DateUtils.TruncateField.QUARTER,
|
truncateField = DateUtils.TruncateField.QUARTER,
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val skippedDaysThisQuarter = entriesForSkip.countSkippedDays(
|
val skippedDaysThisQuarter = entries.countSkippedDays(
|
||||||
truncateField = DateUtils.TruncateField.QUARTER,
|
truncateField = DateUtils.TruncateField.QUARTER
|
||||||
skipDays = habit.skipDays
|
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val valueThisYear = entriesForSum.groupedSum(
|
val valueThisYear = entries.groupedSum(
|
||||||
truncateField = DateUtils.TruncateField.YEAR,
|
truncateField = DateUtils.TruncateField.YEAR,
|
||||||
isNumerical = habit.isNumerical
|
isNumerical = habit.isNumerical
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val skippedDaysThisYear = entriesForSkip.countSkippedDays(
|
val skippedDaysThisYear = entries.countSkippedDays(
|
||||||
truncateField = DateUtils.TruncateField.YEAR,
|
truncateField = DateUtils.TruncateField.YEAR
|
||||||
skipDays = habit.skipDays
|
|
||||||
).firstOrNull()?.value ?: 0
|
).firstOrNull()?.value ?: 0
|
||||||
|
|
||||||
val cal = DateUtils.getStartOfTodayCalendarWithOffset()
|
val cal = DateUtils.getStartOfTodayCalendarWithOffset()
|
||||||
|
|||||||
Reference in New Issue
Block a user