Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveData
import com.google.android.material.button.MaterialButton
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
Expand Down Expand Up @@ -134,6 +136,7 @@ class AddEditReminderDialog : DialogFragment() {
setInitialDeckSelection()
setUpAdvancedDropdown()
setUpCardThresholdInput()
setUpCountCheckboxes()

// For getting the result of the deck selection sub-dialog from ScheduleReminders
// See ScheduleReminders.onDeckSelected for more information
Expand Down Expand Up @@ -264,6 +267,54 @@ class AddEditReminderDialog : DialogFragment() {
}
}

/**
* Convenience data class for setting up the checkboxes for whether to count new, learning, and review cards
* when considering the card trigger threshold.
* @see setUpCountCheckboxes
*/
private data class CountViewsAndActions(
val section: LinearLayout,
val checkbox: MaterialCheckBox,
val actionOnClick: () -> Unit,
val state: LiveData<Boolean>,
)

/**
* Sets up the checkboxes for whether to count new, learning, and review cards when considering the card trigger threshold.
* @see CountViewsAndActions
*/
private fun setUpCountCheckboxes() {
val countViewsAndActionsItems =
listOf(
CountViewsAndActions(
section = contentView.findViewById(R.id.add_edit_reminder_count_new_section),
checkbox = contentView.findViewById(R.id.add_edit_reminder_count_new_checkbox),
actionOnClick = viewModel::toggleCountNew,
state = viewModel.countNew,
),
CountViewsAndActions(
section = contentView.findViewById(R.id.add_edit_reminder_count_lrn_section),
checkbox = contentView.findViewById(R.id.add_edit_reminder_count_lrn_checkbox),
actionOnClick = viewModel::toggleCountLrn,
state = viewModel.countLrn,
),
CountViewsAndActions(
section = contentView.findViewById(R.id.add_edit_reminder_count_rev_section),
checkbox = contentView.findViewById(R.id.add_edit_reminder_count_rev_checkbox),
actionOnClick = viewModel::toggleCountRev,
state = viewModel.countRev,
),
)

countViewsAndActionsItems.forEach { item ->
item.section.setOnClickListener { item.actionOnClick() }
item.checkbox.setOnClickListener { item.actionOnClick() }
item.state.observe(this) { value ->
item.checkbox.isChecked = value
}
}
}

/**
* Show the time picker dialog for selecting a time with a given hour and minute.
* Does not automatically dismiss the old dialog.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,33 @@ class AddEditReminderDialogViewModel(
)
val cardTriggerThreshold: LiveData<Int> = _cardTriggerThreshold

private val _countNew =
MutableLiveData(
when (dialogMode) {
is AddEditReminderDialog.DialogMode.Add -> INITIAL_COUNT_NEW
is AddEditReminderDialog.DialogMode.Edit -> dialogMode.reminderToBeEdited.countNew
},
)
val countNew: LiveData<Boolean> = _countNew

private val _countLrn =
MutableLiveData(
when (dialogMode) {
is AddEditReminderDialog.DialogMode.Add -> INITIAL_COUNT_LRN
is AddEditReminderDialog.DialogMode.Edit -> dialogMode.reminderToBeEdited.countLrn
},
)
val countLrn: LiveData<Boolean> = _countLrn

private val _countRev =
MutableLiveData(
when (dialogMode) {
is AddEditReminderDialog.DialogMode.Add -> INITIAL_COUNT_REV
is AddEditReminderDialog.DialogMode.Edit -> dialogMode.reminderToBeEdited.countRev
},
)
val countRev: LiveData<Boolean> = _countRev

private val _advancedSettingsOpen = MutableLiveData(INITIAL_ADVANCED_SETTINGS_OPEN)
val advancedSettingsOpen: LiveData<Boolean> = _advancedSettingsOpen

Expand All @@ -107,6 +134,21 @@ class AddEditReminderDialogViewModel(
_cardTriggerThreshold.value = threshold
}

fun toggleCountNew() {
Timber.d("Toggled count new from %s", _countNew.value)
_countNew.value = !(_countNew.value ?: false)
}

fun toggleCountLrn() {
Timber.d("Toggled count lrn from %s", _countLrn.value)
_countLrn.value = !(_countLrn.value ?: false)
}

fun toggleCountRev() {
Timber.d("Toggled count rev from %s", _countRev.value)
_countRev.value = !(_countRev.value ?: false)
}

fun toggleAdvancedSettingsOpen() {
Timber.d("Toggled advanced settings open from %s", _advancedSettingsOpen.value)
_advancedSettingsOpen.value = !(_advancedSettingsOpen.value ?: false)
Expand Down Expand Up @@ -136,6 +178,9 @@ class AddEditReminderDialogViewModel(
is AddEditReminderDialog.DialogMode.Add -> true
is AddEditReminderDialog.DialogMode.Edit -> dialogMode.reminderToBeEdited.enabled
},
countNew = countNew.value ?: INITIAL_COUNT_NEW,
countLrn = countLrn.value ?: INITIAL_COUNT_LRN,
countRev = countRev.value ?: INITIAL_COUNT_REV,
)

companion object {
Expand All @@ -153,5 +198,25 @@ class AddEditReminderDialogViewModel(
* We start with it closed to avoid overwhelming the user.
*/
private const val INITIAL_ADVANCED_SETTINGS_OPEN = false

/**
* The default setting for whether new cards are counted when checking the card trigger threshold.
* This value, and the other default settings for whether certain kinds of cards are counted
* when checking the card trigger threshold, are all set to true, as removing some card types
* from card trigger threshold consideration is a form of advanced review reminder customization.
*/
private const val INITIAL_COUNT_NEW = true

/**
* The default setting for whether cards in learning are counted when checking the card trigger threshold.
* @see INITIAL_COUNT_NEW
*/
private const val INITIAL_COUNT_LRN = true

/**
* The default setting for whether cards in review are counted when checking the card trigger threshold.
* @see INITIAL_COUNT_NEW
*/
private const val INITIAL_COUNT_REV = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,16 @@ sealed class ReviewReminderScope : Parcelable {
* Preferably, also add some unit tests to ensure your migration works properly on all user devices once your update is rolled out.
* See ReviewRemindersDatabaseTest for examples on how to do this.
*
* TODO: add remaining fields planned for GSoC 2025.
*
* @param id Unique, auto-incremented ID of the review reminder.
* @param time See [ReviewReminderTime].
* @param cardTriggerThreshold See [ReviewReminderCardTriggerThreshold].
* @param scope See [ReviewReminderScope].
* @param enabled Whether the review reminder's notifications are active or disabled.
* @param profileID ID representing the profile which created this review reminder, as review reminders for
* multiple profiles might be active simultaneously.
* @param countNew Whether new cards are counted when checking the [cardTriggerThreshold].
* @param countLrn Whether learning cards are counted when checking the [cardTriggerThreshold].
* @param countRev Whether review cards are counted when checking the [cardTriggerThreshold].
*/
@Serializable
@Parcelize
Expand All @@ -198,6 +199,9 @@ data class ReviewReminder private constructor(
val scope: ReviewReminderScope,
var enabled: Boolean,
val profileID: String,
val countNew: Boolean,
val countLrn: Boolean,
val countRev: Boolean,
) : Parcelable,
ReviewReminderSchema {
companion object {
Expand All @@ -212,13 +216,19 @@ data class ReviewReminder private constructor(
scope: ReviewReminderScope = ReviewReminderScope.Global,
enabled: Boolean = true,
profileID: String = "",
countNew: Boolean = true,
countLrn: Boolean = true,
countRev: Boolean = true,
) = ReviewReminder(
id = ReviewReminderId.getAndIncrementNextFreeReminderId(),
time,
cardTriggerThreshold,
scope,
enabled,
profileID,
countNew,
countLrn,
countRev,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.ichi2.anki.R
import com.ichi2.anki.canUserAccessDeck
import com.ichi2.anki.common.annotations.LegacyNotifications
import com.ichi2.anki.libanki.Decks
import com.ichi2.anki.libanki.sched.Counts
import com.ichi2.anki.preferences.PENDING_NOTIFICATIONS_ONLY
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.anki.reviewreminders.ReviewReminder
Expand Down Expand Up @@ -104,8 +105,16 @@ class NotificationService : BroadcastReceiver() {
}
}
val dueCardsTotal = dueCardsCount.count()
if (dueCardsTotal < reviewReminder.cardTriggerThreshold.threshold) {
Timber.d("Aborting notification due to threshold: $dueCardsTotal < ${reviewReminder.cardTriggerThreshold.threshold}")
val consideredCardsCount =
Counts().apply {
if (reviewReminder.countNew) addNew(dueCardsCount.new)
if (reviewReminder.countLrn) addLrn(dueCardsCount.lrn)
if (reviewReminder.countRev) addRev(dueCardsCount.rev)
}
val consideredCardsTotal = consideredCardsCount.count()
Timber.d("Due cards count: $dueCardsCount, Considered cards count: $consideredCardsCount")
if (consideredCardsTotal < reviewReminder.cardTriggerThreshold.threshold) {
Timber.d("Aborting notification due to threshold: $consideredCardsTotal < ${reviewReminder.cardTriggerThreshold.threshold}")
return
}

Expand Down
63 changes: 63 additions & 0 deletions AnkiDroid/src/main/res/layout/add_edit_reminder_dialog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,69 @@

</LinearLayout>

<LinearLayout
android:id="@+id/add_edit_reminder_count_new_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/add_edit_reminder_count_new_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/add_edit_reminder_count_new_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Count new cards when checking card threshold"
tools:ignore="HardcodedText" />

</LinearLayout>

<LinearLayout
android:id="@+id/add_edit_reminder_count_lrn_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/add_edit_reminder_count_lrn_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/add_edit_reminder_count_lrn_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Count cards in learning when checking card threshold"
tools:ignore="HardcodedText" />

</LinearLayout>

<LinearLayout
android:id="@+id/add_edit_reminder_count_rev_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/add_edit_reminder_count_rev_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/add_edit_reminder_count_rev_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Count cards in review when checking card threshold"
tools:ignore="HardcodedText" />

</LinearLayout>

</LinearLayout>

</LinearLayout>
Expand Down
Loading