Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature: allow users to automatically skip certain days of the week #2012

Open
wants to merge 24 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
org.gradle.java.installations.auto-download=true
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ package org.isoron.uhabits.activities.common.dialogs
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatDialogFragment
import org.isoron.platform.gui.AndroidDataView
import org.isoron.platform.time.JavaLocalDateFormatter
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.AndroidThemeSwitcher
import org.isoron.uhabits.core.commands.BlockSkippedDayCommand
import org.isoron.uhabits.core.commands.Command
import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.Habit
Expand All @@ -36,6 +38,7 @@ import org.isoron.uhabits.core.ui.views.HistoryChart
import org.isoron.uhabits.core.ui.views.LightTheme
import org.isoron.uhabits.core.ui.views.OnDateClickedListener
import org.isoron.uhabits.core.utils.DateUtils
import org.isoron.uhabits.utils.showMessage
import java.util.Locale
import kotlin.math.min

Expand Down Expand Up @@ -119,9 +122,20 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
}

override fun onCommandFinished(command: Command) {
val msg = getExecuteString(command)
if (msg != null) (dataView as View).showMessage(msg)
refreshData()
}

private fun getExecuteString(command: Command): String? {
return when (command) {
is BlockSkippedDayCommand -> {
getString(R.string.toast_day_auto_skip)
}
else -> null
}
}

companion object {
// HistoryEditorDialog handles multiple dialogs on its own,
// because sometimes we want it to be shown under another dialog (e.g. NumberPopup)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import org.isoron.uhabits.core.models.HabitType
import org.isoron.uhabits.core.models.NumericalHabitType
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.SkipDays
import org.isoron.uhabits.core.models.WeekdayList
import org.isoron.uhabits.databinding.ActivityEditHabitBinding
import org.isoron.uhabits.utils.applyRootViewInsets
Expand Down Expand Up @@ -81,6 +82,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 @@ -106,6 +109,8 @@ class EditHabitActivity : AppCompatActivity() {
color = habit.color
freqNum = habit.frequency.numerator
freqDen = habit.frequency.denominator
isSkipDays = habit.skipDays.isSkipDays
listSkipDays = habit.skipDays.days
targetType = habit.targetType
habit.reminder?.let {
reminderHour = it.hour
Expand All @@ -127,6 +132,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 @@ -243,12 +250,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 @@ -279,6 +305,7 @@ class EditHabitActivity : AppCompatActivity() {
}

habit.frequency = Frequency(freqNum, freqDen)
habit.skipDays = SkipDays(isSkipDays, listSkipDays)
if (habitType == HabitType.NUMERICAL) {
habit.targetValue = binding.targetInput.text.toString().toDouble()
habit.targetType = targetType
Expand Down Expand Up @@ -315,6 +342,20 @@ class EditHabitActivity : AppCompatActivity() {
isValid = false
}
}
if (isSkipDays) {
if (habitType == HabitType.YES_NO && freqDen == 7) {
if (7 - listSkipDays.numDays() < freqNum) {
binding.skipDaysPicker.text = getString(R.string.validation_too_many_skips)
binding.skipDaysPicker.error = getFormattedValidationError(R.string.validation_too_many_skips)
isValid = false
}
}
if (listSkipDays.numDays() == 7) {
binding.skipDaysPicker.text = getString(R.string.validation_too_many_skips)
binding.skipDaysPicker.error = getFormattedValidationError(R.string.validation_too_many_skips)
isValid = false
}
}
return isValid
}

Expand All @@ -332,6 +373,22 @@ class EditHabitActivity : AppCompatActivity() {
}
}

private fun populateSkipDays() {
val preferences = (application as HabitsApplication).component.preferences
if (preferences.isSkipEnabled && (freqDen == 1 || freqDen == 7)) {
binding.skipDaysOuterBox.visibility = View.VISIBLE
} else {
isSkipDays = false
listSkipDays = WeekdayList.NO_DAY
binding.skipDaysOuterBox.visibility = View.GONE
}
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 All @@ -341,6 +398,7 @@ class EditHabitActivity : AppCompatActivity() {
30 -> getString(R.string.every_month)
else -> "$freqNum/$freqDen"
}
populateSkipDays()
}

private fun populateTargetType() {
Expand Down Expand Up @@ -373,6 +431,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
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import org.isoron.uhabits.activities.common.dialogs.NumberDialog
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
import org.isoron.uhabits.core.commands.ArchiveHabitsCommand
import org.isoron.uhabits.core.commands.BlockSkippedDayCommand
import org.isoron.uhabits.core.commands.ChangeHabitColorCommand
import org.isoron.uhabits.core.commands.Command
import org.isoron.uhabits.core.commands.CommandRunner
Expand Down Expand Up @@ -321,6 +322,9 @@ class ListHabitsScreen
command.selected.size
)
}
is BlockSkippedDayCommand -> {
return activity.resources.getString(R.string.toast_day_auto_skip)
}
else -> return null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ class CheckmarkButtonView(
}

fun performToggle() {
value = Entry.nextToggleValue(
val next_value = Entry.nextToggleValue(
value = value,
isSkipEnabled = preferences.isSkipEnabled,
areQuestionMarksEnabled = preferences.areQuestionMarksEnabled
)
onToggle(value, notes)
onToggle(next_value, notes)
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
invalidate()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
private val scope = CoroutineScope(Dispatchers.Main)
private lateinit var presenter: ShowHabitPresenter
private val screen = Screen()
val activity = (this as AppCompatActivity)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
import org.isoron.uhabits.utils.InterfaceUtils
import org.isoron.uhabits.utils.formatTime
import org.isoron.uhabits.utils.toFormattedString

class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {

Expand Down Expand Up @@ -78,6 +79,12 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
if (state.question.isEmpty()) {
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()
}
Expand Down
20 changes: 20 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 @@ -76,6 +76,7 @@
android:paddingRight="4dp">

<!-- Title and color -->

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down Expand Up @@ -252,6 +253,25 @@


<!-- Notes -->
<FrameLayout
android:id="@+id/skipDaysOuterBox"
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
23 changes: 23 additions & 0 deletions uhabits-android/src/main/res/layout/show_habit_subtitle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,27 @@
android:textSize="@dimen/smallTextSize" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="2dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:visibility="visible">

<TextView
android:id="@+id/skipLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="16dp"
android:paddingTop="1dp"
android:text=""
android:textColor="?attr/contrast60"
android:textSize="@dimen/smallTextSize" />

</LinearLayout>


</merge>
4 changes: 4 additions & 0 deletions uhabits-android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,21 @@
<item quantity="one">Habit unarchived</item>
<item quantity="other">Habits unarchived</item>
</plurals>
<string name="toast_day_auto_skip">Day automatically skipped</string>
<string name="title_activity_show_habit" translatable="false"/>
<string name="overview">Overview</string>
<string name="habit_strength">Habit strength</string>
<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 Expand Up @@ -217,6 +220,7 @@
<string name="measurable_units_example">e.g. miles</string>
<string name="every_month">Every month</string>
<string name="validation_cannot_be_blank">Cannot be blank</string>
<string name="validation_too_many_skips">Too many days selected</string>
<string name="today">Today</string>
<string name="enter">Enter</string>
<string name="no_habits">No habits found</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 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
2 changes: 2 additions & 0 deletions uhabits-core-legacy/assets/main/migrations/024.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
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
Loading
Loading