Skip to content

Commit

Permalink
Skip days implemented. Scores not correct yet
Browse files Browse the repository at this point in the history
  • Loading branch information
Dharanish committed May 18, 2024
1 parent 9bfae97 commit ec08b60
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class EditHabitActivity : AppCompatActivity() {
var androidColor = 0
var freqNum = 1
var freqDen = 1
var isSkipDays = false
var listSkipDays: WeekdayList = WeekdayList.NO_DAY
var reminderHour = -1
var reminderMin = -1
var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
Expand All @@ -104,6 +106,8 @@ class EditHabitActivity : AppCompatActivity() {
color = habit.color
freqNum = habit.frequency.numerator
freqDen = habit.frequency.denominator
isSkipDays = habit.skipDays
listSkipDays = habit.skipDaysList
targetType = habit.targetType
habit.reminder?.let {
reminderHour = it.hour
Expand All @@ -125,6 +129,8 @@ class EditHabitActivity : AppCompatActivity() {
color = PaletteColor(state.getInt("paletteColor"))
freqNum = state.getInt("freqNum")
freqDen = state.getInt("freqDen")
isSkipDays = state.getBoolean("isSkipDays", false)
listSkipDays = WeekdayList(state.getInt("listSkipDays", 0))
reminderHour = state.getInt("reminderHour")
reminderMin = state.getInt("reminderMin")
reminderDays = WeekdayList(state.getInt("reminderDays"))
Expand Down Expand Up @@ -241,12 +247,31 @@ class EditHabitActivity : AppCompatActivity() {
dialog.setListener { days: WeekdayList ->
reminderDays = days
if (reminderDays.isEmpty) reminderDays = WeekdayList.EVERY_DAY
if (isSkipDays) reminderDays = WeekdayList(reminderDays.toArray(),listSkipDays.toArray())
populateReminder()
}
dialog.setSelectedDays(reminderDays)
dialog.dismissCurrentAndShow(supportFragmentManager, "dayPicker")
}

populateSkipDays()
binding.skipDaysPicker.setOnClickListener {
val dialog = WeekdayPickerDialog()

dialog.setListener { days: WeekdayList ->
listSkipDays = days
if (listSkipDays.isEmpty) listSkipDays = WeekdayList.NO_DAY
isSkipDays = (listSkipDays != WeekdayList.NO_DAY)
if (reminderHour >= 0 && isSkipDays) {
reminderDays = WeekdayList(reminderDays.toArray(),listSkipDays.toArray())
populateReminder()
}
populateSkipDays()
}
dialog.setSelectedDays(listSkipDays)
dialog.dismissCurrentAndShow(supportFragmentManager, "dayPicker")
}

binding.buttonSave.setOnClickListener {
if (validate()) save()
}
Expand Down Expand Up @@ -277,6 +302,8 @@ class EditHabitActivity : AppCompatActivity() {
}

habit.frequency = Frequency(freqNum, freqDen)
habit.skipDays = isSkipDays
habit.skipDaysList = listSkipDays
if (habitType == HabitType.NUMERICAL) {
habit.targetValue = binding.targetInput.text.toString().toDouble()
habit.targetType = targetType
Expand Down Expand Up @@ -330,6 +357,14 @@ class EditHabitActivity : AppCompatActivity() {
}
}

private fun populateSkipDays() {
if (isSkipDays) {
binding.skipDaysPicker.text = listSkipDays.toFormattedString(this)
} else {
binding.skipDaysPicker.text = getString(R.string.skip_days_off)
}
}

@SuppressLint("StringFormatMatches")
private fun populateFrequency() {
binding.booleanFrequencyPicker.text = formatFrequency(freqNum, freqDen, resources)
Expand Down Expand Up @@ -372,6 +407,8 @@ class EditHabitActivity : AppCompatActivity() {
putInt("androidColor", androidColor)
putInt("freqNum", freqNum)
putInt("freqDen", freqDen)
putBoolean("isSkipDays", isSkipDays)
putInt("listSkipDays", listSkipDays.toInteger())
putInt("reminderHour", reminderHour)
putInt("reminderMin", reminderMin)
putInt("reminderDays", reminderDays.toInteger())
Expand Down
19 changes: 19 additions & 0 deletions uhabits-android/src/main/res/layout/activity_edit_habit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
android:paddingRight="4dp">

<!-- Title and color -->

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down Expand Up @@ -249,6 +250,24 @@


<!-- Notes -->
<FrameLayout
style="@style/FormOuterBox"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout style="@style/FormInnerBox">

<TextView
style="@style/FormLabel"
android:text="@string/skip_days" />

<TextView
android:id="@+id/skipDaysPicker"
style="@style/FormDropdown"
android:text="@string/skip_days_off" />
</LinearLayout>
</FrameLayout>

<FrameLayout style="@style/FormOuterBox">
<LinearLayout style="@style/FormInnerBox">
<TextView
Expand Down
2 changes: 2 additions & 0 deletions uhabits-android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@
<string name="history">History</string>
<string name="clear">Clear</string>
<string name="reminder">Reminder</string>
<string name="skip_days">Skip days</string>
<string name="save">Save</string>
<string name="streaks">Streaks</string>
<string name="no_habits_found">You have no active habits</string>
<string name="no_habits_left_to_do">You\'re all done for today!</string>
<string name="long_press_to_toggle">Press-and-hold to check or uncheck</string>
<string name="reminder_off">Off</string>
<string name="skip_days_off">Off</string>
<string name="create_habit">Create habit</string>
<string name="edit_habit">Edit habit</string>
<string name="check">Check</string>
Expand Down
2 changes: 1 addition & 1 deletion uhabits-core-legacy/assets/main/migrations/009.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
create table Habits ( id integer primary key autoincrement, archived integer, color integer, description text, freq_den integer, freq_num integer, highlight integer, name text, position integer, reminder_hour integer, reminder_min integer )
create table Habits ( id integer primary key autoincrement, archived integer, color integer, description text, freq_den integer, freq_num integer, skip_days integer, skip_days_list integer, highlight integer, name text, position integer, reminder_hour integer, reminder_min integer )
create table Checkmarks ( id integer primary key autoincrement, habit integer references habits(id), timestamp integer, value integer )
create table Repetitions ( id integer primary key autoincrement, habit integer references habits(id), timestamp integer )
create table Streak ( id integer primary key autoincrement, end integer, habit integer references habits(id), length integer, start integer )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import org.isoron.platform.io.nextId
class HabitRepository(var db: Database) {

companion object {
const val SELECT_COLUMNS = "id, name, description, freq_num, freq_den, color, archived, position, unit, target_value, type"
const val SELECT_PLACEHOLDERS = "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
const val UPDATE_COLUMNS = "id=?, name=?, description=?, freq_num=?, freq_den=?, color=?, archived=?, position=?, unit=?, target_value=?, type=?"
const val SELECT_COLUMNS = "id, name, description, freq_num, freq_den, skip_days, skip_days_list, color, archived, position, unit, target_value, type"
const val SELECT_PLACEHOLDERS = "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
const val UPDATE_COLUMNS = "id=?, name=?, description=?, freq_num=?, freq_den=?, skip_days=?, skip_days_list=?, color=?, archived=?, position=?, unit=?, target_value=?, type=?"
}

private val findAllStatement = db.prepareStatement("select $SELECT_COLUMNS from habits order by position")
Expand Down Expand Up @@ -60,7 +60,7 @@ class HabitRepository(var db: Database) {

fun update(habit: Habit) {
bindHabitToStatement(habit, updateStatement)
updateStatement.bindInt(11, habit.id)
updateStatement.bindInt(13, habit.id)
updateStatement.step()
updateStatement.reset()
}
Expand All @@ -70,12 +70,14 @@ class HabitRepository(var db: Database) {
name = stmt.getText(1),
description = stmt.getText(2),
frequency = Frequency(stmt.getInt(3), stmt.getInt(4)),
color = PaletteColor(stmt.getInt(5)),
isArchived = stmt.getInt(6) != 0,
position = stmt.getInt(7),
unit = stmt.getText(8),
target = stmt.getReal(9),
type = if (stmt.getInt(10) == 0) HabitType.BOOLEAN_HABIT else HabitType.NUMERICAL_HABIT)
skipDays = (stmt.getInt(5) == 1),
skipDaysList = WeekDayList(stmt.getInt(6)),
color = PaletteColor(stmt.getInt(7)),
isArchived = stmt.getInt(8) != 0,
position = stmt.getInt(9),
unit = stmt.getText(10),
target = stmt.getReal(11),
type = if (stmt.getInt(12) == 0) HabitType.BOOLEAN_HABIT else HabitType.NUMERICAL_HABIT)
}

private fun bindHabitToStatement(habit: Habit, statement: PreparedStatement) {
Expand All @@ -84,12 +86,14 @@ class HabitRepository(var db: Database) {
statement.bindText(2, habit.description)
statement.bindInt(3, habit.frequency.numerator)
statement.bindInt(4, habit.frequency.denominator)
statement.bindInt(5, habit.color.index)
statement.bindInt(6, if (habit.isArchived) 1 else 0)
statement.bindInt(7, habit.position)
statement.bindText(8, habit.unit)
statement.bindReal(9, habit.target)
statement.bindInt(10, habit.type.code)
statement.bindInt(5, if (habit.skipDays) 1 else 0)
statement.bindInt(6, habit.skipDaysList.toInteger())
statement.bindInt(7, habit.color.index)
statement.bindInt(8, if (habit.isArchived) 1 else 0)
statement.bindInt(9, habit.position)
statement.bindText(10, habit.unit)
statement.bindReal(11, habit.target)
statement.bindInt(12, habit.type.code)
}

fun delete(habit: Habit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ open class EntryList {

/**
* Returns the entry corresponding to the given timestamp. If no entry with such timestamp
* has been previously added, returns Entry(timestamp, UNKNOWN).
* has been previously added, returns Entry(timestamp, UNKNOWN). or Entry(timestamp, SKIP) if
* skip days are enabled and that day is to be skipped
*/
@Synchronized
open fun get(timestamp: Timestamp): Entry {
return entriesByTimestamp[timestamp] ?: Entry(timestamp, UNKNOWN)
open fun get(timestamp: Timestamp, skipDays: Boolean = false, skipDaysList: WeekdayList = WeekdayList.NO_DAY): Entry {
return entriesByTimestamp[timestamp] ?: if (skipDays && skipDaysList.isDayTrue(timestamp.weekday)) Entry(timestamp, SKIP) else Entry(timestamp, UNKNOWN)
}

/**
Expand All @@ -51,12 +52,12 @@ open class EntryList {
* included.
*/
@Synchronized
open fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
open fun getByInterval(from: Timestamp, to: Timestamp, skipDays: Boolean = false, skipDaysList: WeekdayList = WeekdayList.NO_DAY): List<Entry> {
val result = mutableListOf<Entry>()
if (from.isNewerThan(to)) return result
var current = to
while (current >= from) {
result.add(get(current))
result.add(get(current, skipDays, skipDaysList))
current = current.minus(1)
}
return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package org.isoron.uhabits.core.models

data class Frequency(
var numerator: Int,
var denominator: Int
var denominator: Int,
) {
init {
if (numerator == denominator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ data class Habit(
var color: PaletteColor = PaletteColor(8),
var description: String = "",
var frequency: Frequency = Frequency.DAILY,
var skipDays: Boolean = false,
var skipDaysList: WeekdayList = WeekdayList.NO_DAY,
var id: Long? = null,
var isArchived: Boolean = false,
var name: String = "",
Expand Down Expand Up @@ -90,6 +92,8 @@ data class Habit(
scores.recompute(
frequency = frequency,
isNumerical = isNumerical,
skipDays = skipDays,
skipDaysList = skipDaysList,
numericalHabitType = targetType,
targetValue = targetValue,
computedEntries = computedEntries,
Expand All @@ -108,6 +112,8 @@ data class Habit(
this.color = other.color
this.description = other.description
this.frequency = other.frequency
this.skipDays = other.skipDays
this.skipDaysList = other.skipDaysList
// this.id should not be copied
this.isArchived = other.isArchived
this.name = other.name
Expand All @@ -128,6 +134,8 @@ data class Habit(
if (color != other.color) return false
if (description != other.description) return false
if (frequency != other.frequency) return false
if (skipDays != other.skipDays) return false
if (skipDaysList != other.skipDaysList) return false
if (id != other.id) return false
if (isArchived != other.isArchived) return false
if (name != other.name) return false
Expand All @@ -147,6 +155,7 @@ data class Habit(
var result = color.hashCode()
result = 31 * result + description.hashCode()
result = 31 * result + frequency.hashCode()
result = 31 * result + skipDaysList.hashCode()
result = 31 * result + (id?.hashCode() ?: 0)
result = 31 * result + isArchived.hashCode()
result = 31 * result + name.hashCode()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class ScoreList {
fun recompute(
frequency: Frequency,
isNumerical: Boolean,
skipDays: Boolean,
skipDaysList: WeekdayList,
numericalHabitType: NumericalHabitType,
targetValue: Double,
computedEntries: EntryList,
Expand All @@ -79,7 +81,7 @@ class ScoreList {
var numerator = frequency.numerator
var denominator = frequency.denominator
val freq = frequency.toDouble()
val values = computedEntries.getByInterval(from, to).map { it.value }.toIntArray()
val values = computedEntries.getByInterval(from, to, skipDays, skipDaysList).map { it.value }.toIntArray()
val isAtMost = numericalHabitType == NumericalHabitType.AT_MOST

// For non-daily boolean habits, we double the numerator and the denominator to smooth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class WeekdayList {
this.weekdays = Arrays.copyOf(weekdays, 7)
}

constructor(addDays: BooleanArray, removeDays: BooleanArray) {
weekdays = BooleanArray(7)
for (i in 0..6) {
weekdays[i] = addDays[i] && !removeDays[i]
}
}

val isEmpty: Boolean
get() {
for (d in weekdays) if (d) return false
Expand All @@ -58,6 +65,10 @@ class WeekdayList {
return packedList
}

fun isDayTrue(dayNum: Int): Boolean {
return weekdays[dayNum]
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
Expand All @@ -73,5 +84,6 @@ class WeekdayList {

companion object {
val EVERY_DAY = WeekdayList(127)
val NO_DAY = WeekdayList(booleanArrayOf(false, false, false, false, false, false, false))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.models.EntryList
import org.isoron.uhabits.core.models.Frequency
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.models.WeekdayList
import org.isoron.uhabits.core.models.sqlite.records.EntryRecord

class SQLiteEntryList(database: Database) : EntryList() {
Expand All @@ -43,14 +44,13 @@ class SQLiteEntryList(database: Database) : EntryList() {
isLoaded = true
}

override fun get(timestamp: Timestamp): Entry {
override fun get(timestamp: Timestamp, skipDays: Boolean, skipDaysList: WeekdayList): Entry {
loadRecords()
return super.get(timestamp)
return super.get(timestamp, skipDays, skipDaysList)
}

override fun getByInterval(from: Timestamp, to: Timestamp): List<Entry> {
override fun getByInterval(from: Timestamp, to: Timestamp, skipDays: Boolean, skipDaysList: WeekdayList): List<Entry> {
loadRecords()
return super.getByInterval(from, to)
return super.getByInterval(from, to, skipDays, skipDaysList)
}

override fun add(entry: Entry) {
Expand Down
Loading

0 comments on commit ec08b60

Please sign in to comment.