-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
feat(reminders): threshold filters #19156
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
base: main
Are you sure you want to change the base?
feat(reminders): threshold filters #19156
Conversation
1ca08cc to
0793e7a
Compare
0793e7a to
f2c9dde
Compare
|
5a3ddeb to
a390cfb
Compare
|
a390cfb to
9a53408
Compare
|
9a53408 to
ae683e5
Compare
|
Forgot to stage some code. Fixed. |
ae683e5 to
e128d8a
Compare
|
Rebased, fully unblocked, and ready for review! |
|
I've decided to block this behind #19157, as some of this PR's code will need to be updated once that guy's merged. |
e128d8a to
74874af
Compare
74874af to
d022fa4
Compare
Unblocked and ready to review! |
d022fa4 to
8866c30
Compare
|
Thanks for the test patch! I've decided to go the full mile and add a few more helper functions on top, hopefully the file is more readable now. As for the UI coloring, I love the idea. It was a bit tricky to implement in a way that would work with the translation system, but this is what I came up with: // Manually split the string resource so that we can color just the review state part
val (reviewState, colorAttr) = REVIEW_STATE_STRINGS_AND_COLORS.entries.elementAt(i)
val splitString = getString(R.string.review_reminders_include_review_state_for_threshold_do_not_translate).split("%s")
item.textView.text =
buildSpannedString {
append(splitString[0])
color(MaterialColors.getColor(requireContext(), colorAttr, 0)) {
append(getString(reviewState))
}
append(splitString[1])
}Hopefully that works. I opted for no underline because it made it look clickable and the English text I've used is taken from upstream's wording; attached below is an image of the new UI. Should I move the new strings to a different file? Thoughts on the styling? Is it more readable now? |
GSoC 2025: Review Reminders Add a group of new advanced review reminder options: count new cards, count cards in learning, and count cards in review. When the review reminder is about to send a notification and checks to see if the amount of cards in the deck is greater than the card trigger threshold, it examines these options to check if it should count and consider new cards, cards in learning, and cards in review. Adds three new checkboxes to the AddEditReminderDialog to toggle these booleans on or off, with colored text for the corresponding review state boolean. Edits some logic in NotificationService to add up cards only from selected card type when determining whether the card trigger threshold is met. Adds three new boolean fields to store the states of these settings to ReviewReminder. Adds unit tests. Modifies some unit test utilities for convenience.
8866c30 to
f4af44f
Compare
|
I'd strongly prefer lowercase: Check with the other mentors on wording, but I'd prefer shorter text:
|
david-allison
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, thanks so much!!
AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/AddEditReminderDialog.kt
Show resolved
Hide resolved
|
|
||
| // Manually split the string resource so that we can color just the review state part | ||
| val (reviewState, colorAttr) = REVIEW_STATE_STRINGS_AND_COLORS.entries.elementAt(i) | ||
| val splitString = getString(R.string.review_reminders_include_review_state_for_threshold_do_not_translate).split("%s") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome.
I'd consider defining partition (pseudocode below, needs docs, maybe tests).
- Minor bug that if
%sis missing, things blow up - probably fine to ignore - Minor bug that the string is truncated if there are too many
%s- fixed below - Ensure that a change:
%sto%1$swould fail a unit test
fun String.partition(delimiter: String): Pair<String, String> = this.split(delimiter, limit = 2)
val (before, after) = "a %s b".partition("%s")There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, elegant! Implemented, I've added unit tests too.
| /** | ||
| * Convenience method for chaining [addDeck] and [addNoteToDeck]. | ||
| * | ||
| * Usage: `val deckId = addDeck("My Deck").withNote(count = 5, queueType = QueueType.New)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
give naming a second consideration, maybe even define withNote to mean 1, and withNotes to mean more than 1.
Subject: [PATCH]
---
Index: AnkiDroid/src/test/java/com/ichi2/anki/services/NotificationServiceTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/services/NotificationServiceTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/services/NotificationServiceTest.kt
--- a/AnkiDroid/src/test/java/com/ichi2/anki/services/NotificationServiceTest.kt (revision f4af44f18bcf1ad2a3348d3ec51a9e8d3b314072)
+++ b/AnkiDroid/src/test/java/com/ichi2/anki/services/NotificationServiceTest.kt (date 1768720363077)
@@ -87,7 +87,7 @@
@Test
fun `onReceive with less cards than card threshold should not fire notification but schedule next`() =
runTest {
- val did1 = addDeck("Deck", setAsSelected = true).withNote(count = 2)
+ val did1 = addDeck("Deck", setAsSelected = true).withNotes(count = 2)
val reviewReminderDeckSpecific = createTestReminder(deckId = did1, thresholdInt = 3)
val reviewReminderAppWide = createTestReminder(thresholdInt = 3)
ReviewRemindersDatabase.storeReminders(reviewReminderDeckSpecific, reviewReminderAppWide)
@@ -103,7 +103,7 @@
@Test
fun `onReceive with happy path for single deck should fire notification and schedule next`() =
runTest {
- val did1 = addDeck("Deck", setAsSelected = true).withNote(count = 2)
+ val did1 = addDeck("Deck", setAsSelected = true).withNotes(count = 2)
val reviewReminder = createAndSaveDummyDeckSpecificReminder(did1)
triggerDummyReminderNotification(reviewReminder)
@@ -115,8 +115,8 @@
@Test
fun `onReceive with happy path for global reminder should fire notification and schedule next`() =
runTest {
- addDeck("Deck1").withNote(count = 2)
- addDeck("Deck2").withNote(count = 2)
+ addDeck("Deck1").withNotes(count = 2)
+ addDeck("Deck2").withNotes(count = 2)
val reviewReminder = createTestReminder(thresholdInt = 4)
triggerDummyReminderNotification(reviewReminder)
@@ -210,7 +210,7 @@
@Test
fun `onReceive with blocked collection should not fire notification but schedule next`() =
runTest {
- val did1 = addDeck("Deck", setAsSelected = true).withNote(count = 2)
+ val did1 = addDeck("Deck", setAsSelected = true).withNotes(count = 2)
val reviewReminderDeckSpecific = createAndSaveDummyDeckSpecificReminder(did1)
val reviewReminderAppWide = createAndSaveDummyAppWideReminder()
@@ -226,7 +226,7 @@
@Test
fun `onReceive with snoozed notification should fire notification but not schedule next`() =
runTest {
- val did1 = addDeck("Deck", setAsSelected = true).withNote(count = 2)
+ val did1 = addDeck("Deck", setAsSelected = true).withNotes(count = 2)
val reviewReminderDeckSpecific = createAndSaveDummyDeckSpecificReminder(did1)
val reviewReminderAppWide = createAndSaveDummyAppWideReminder()
@@ -243,7 +243,7 @@
@Test
fun `onReceive with rev cards not counted and only rev cards present should not fire notification`() =
runTest {
- val did1 = addDeck("Deck", setAsSelected = true).withNote(count = 2, queueType = QueueType.Rev)
+ val did1 = addDeck("Deck", setAsSelected = true).withNotes(count = 2, queueType = QueueType.Rev)
val reviewReminder = createTestReminder(deckId = did1, countRev = false)
ReviewRemindersDatabase.storeReminders(reviewReminder)
@@ -255,7 +255,7 @@
@Test
fun `onReceive with new cards not counted and only new cards present should not fire notification`() =
runTest {
- val did1 = addDeck("Deck").withNote(count = 2)
+ val did1 = addDeck("Deck").withNotes(count = 2)
val reviewReminder = createTestReminder(deckId = did1, countNew = false)
ReviewRemindersDatabase.storeReminders(reviewReminder)
@@ -267,7 +267,7 @@
@Test
fun `onReceive with lrn cards not counted and only lrn cards present should not fire notification`() =
runTest {
- val did1 = addDeck("Deck").withNote(count = 2, queueType = QueueType.Lrn)
+ val did1 = addDeck("Deck").withNotes(count = 2, queueType = QueueType.Lrn)
val reviewReminder = createTestReminder(deckId = did1, countLrn = false)
ReviewRemindersDatabase.storeReminders(reviewReminder)
@@ -319,8 +319,8 @@
@Test
fun `snooze actions of different notifications and different intervals should be different`() =
runTest {
- val did1 = addDeck("Deck1").withNote(count = 2)
- val did2 = addDeck("Deck2").withNote(count = 2)
+ val did1 = addDeck("Deck1").withNotes(count = 2)
+ val did2 = addDeck("Deck2").withNotes(count = 2)
val reviewReminderOne = createTestReminder(deckId = did1, thresholdInt = 1)
val reviewReminderTwo = createTestReminder(deckId = did2, thresholdInt = 1)
ReviewRemindersDatabase.storeReminders(reviewReminderOne, reviewReminderTwo)
Index: libanki/testutils/src/main/java/com/ichi2/anki/libanki/testutils/AnkiTest.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/libanki/testutils/src/main/java/com/ichi2/anki/libanki/testutils/AnkiTest.kt b/libanki/testutils/src/main/java/com/ichi2/anki/libanki/testutils/AnkiTest.kt
--- a/libanki/testutils/src/main/java/com/ichi2/anki/libanki/testutils/AnkiTest.kt (revision f4af44f18bcf1ad2a3348d3ec51a9e8d3b314072)
+++ b/libanki/testutils/src/main/java/com/ichi2/anki/libanki/testutils/AnkiTest.kt (date 1768720430579)
@@ -226,10 +226,18 @@
/**
* Convenience method for chaining [addDeck] and [addNoteToDeck].
*
- * Usage: `val deckId = addDeck("My Deck").withNote(count = 5, queueType = QueueType.New)`
+ * Usage: `val deckId = addDeck("My Deck").withNote(queueType = QueueType.New)`
*/
- fun DeckId.withNote(
- count: Int = 1,
+ fun DeckId.withNote(queueType: QueueType = QueueType.New): DeckId
+ = withNotes(count = 1, queueType = queueType)
+
+ /**
+ * Convenience method for chaining [addDeck] and [addNoteToDeck].
+ *
+ * Usage: `val deckId = addDeck("My Deck").withNotes(count = 5, queueType = QueueType.New)`
+ */
+ fun DeckId.withNotes(
+ count: Int,
queueType: QueueType = QueueType.New,
): DeckId =
this.apply {|
I've decided to keep the existing text ("Include [type] cards for card threshold") for now and will create an issue later to address possibly changing it to be more precise. I think that we should probably err on the side of verbosity to ensure users and translators understand what the string is supposed to mean. I've implemented making the text lowercase, too. Due to #20163, we're putting this PR on hold until migrations on the main branch are stable. I'll also change this code to have the proper migration. |

Purpose / Description
Add a group of new advanced review reminder options: count new cards, count cards in learning, and count cards in review. When the review reminder is about to send a notification and checks to see if the amount of cards in the deck is greater than the card trigger threshold, it examines these options to check if it should count and consider new cards, cards in learning, and cards in review.
Adds three new checkboxes to the AddEditReminderDialog to toggle these booleans on or off. Edits some logic in NotificationService to add up cards only from selected card type when determining whether the card trigger threshold is met.
Adds three new boolean fields to store the states of these settings to ReviewReminder. Adds unit tests.
UI
Fixes
Approach
One of the two planned review reminder advanced options! Just needs some extra checks when handling the
Countobjects.How Has This Been Tested?
Checklist