ankidroid / ankidroid/Anki-Android

CardContentProvider.answerCard cannot correctly handle non-top cards

Open
#18,270 3 comments 0 reactions 0 assignees View on GitHub
API Blocked by dependency Keep Open upstream-issue
Dominant language
Kotlin
Stars
11.8k
Forks
2.9k
Avg merge
2d 3h
Merged PRs (30d)
171

Description

### Checked for duplicates?

- [x] This issue is not a duplicate

### Does it also happen in the desktop version?

- [x] This bug does not occur in the latest version of Anki Desktop

### What are the steps to reproduce this bug?

1. An external application uses the CardContentProvider to update the review status of a card via the content://com.ichi2.anki.flashcards/schedule URI.
2. The target card, identified by NOTE_ID and CARD_ORD in the ContentValues, is not the currently active card in the AnkiDroid reviewer queue (i.e., not the topCard in the Scheduler.currentQueueState()).
3. The external app calls update on the SCHEDULE URI, providing the card identifiers and an EASE value (e.g., FlashCardsContract.ReviewInfo.EASE).
4. Buggy Behaviour: The previous implementation might incorrectly fetch the scheduling state of the current top card from the scheduler queue instead of the target card's actual state, or use a legacy answerCard method. This leads to the target card being reviewed with incorrect parameters, potentially messing up its scheduling interval.

### Expected behaviour

When an external application updates a card's review status via the CardContentProvider (SCHEDULE URI):
- The provider should determine if the target card matches the current top card in the scheduler's queue.
- If it matches, the provider should use the readily available CurrentQueueState from the scheduler.
- If it does not match (or the queue is empty), the provider must fetch the specific SchedulingStates for the target card.
- In either case, the provider should use the correct card and its corresponding scheduling state to call the modern Scheduler.answerCard(info: CurrentQueueState, ease: Int) method.
- This ensures the target card is always answered based on its own state and the provided ease level, resulting in the correct scheduling update.

### Debug info

```text
AnkiDroid 2.20.1

(actually looked into the code, all versions has the same problem)
04-30 16:28:34.834 27079 27088 E AnkiDroid: ContentProvider/ answerCard - RuntimeException on answering card
04-30 16:28:34.834 27079 27088 E AnkiDroid: q7.d: card was modified: Normal(
04-30 16:28:34.834 27079 27088 E AnkiDroid: Review(
04-30 16:28:34.834 27079 27088 E AnkiDroid: ReviewState {
04-30 16:28:34.834 27079 27088 E AnkiDroid: scheduled_days: 1,
04-30 16:28:34.834 27079 27088 E AnkiDroid: elapsed_days: 10,
04-30 16:28:34.834 27079 27088 E AnkiDroid: ease_factor: 2.5,
04-30 16:28:34.834 27079 27088 E AnkiDroid: lapses: 0,
04-30 16:28:34.834 27079 27088 E AnkiDroid: leeched: false,
04-30 16:28:34.834 27079 27088 E AnkiDroid: memory_state: None,
04-30 16:28:34.834 27079 27088 E AnkiDroid: },
04-30 16:28:34.834 27079 27088 E AnkiDroid: ),
04-30 16:28:34.834 27079 27088 E AnkiDroid: ) Normal(
04-30 16:28:34.834 27079 27088 E AnkiDroid: Review(
04-30 16:28:34.834 27079 27088 E AnkiDroid: ReviewState {
04-30 16:28:34.834 27079 27088 E AnkiDroid: scheduled_days: 2,
04-30 16:28:34.834 27079 27088 E AnkiDroid: elapsed_days: 5,
04-30 16:28:34.834 27079 27088 E AnkiDroid: ease_factor: 2.35,
04-30 16:28:34.834 27079 27088 E AnkiDroid: lapses: 0,
04-30 16:28:34.834 27079 27088 E AnkiDroid: leeched: false,
04-30 16:28:34.834 27079 27088 E AnkiDroid: memory_state: None,
04-30 16:28:34.834 27079 27088 E AnkiDroid: },
04-30 16:28:34.834 27079 27088 E AnkiDroid: ),
04-30 16:28:34.834 27079 27088 E AnkiDroid: )
04-30 16:28:34.834 27079 27088 E AnkiDroid: at F8.i.b(SourceFile:362)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at o7.a.i0(SourceFile:23)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at Q4.g.a(SourceFile:52)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at com.ichi2.anki.provider.CardContentProvider.update(SourceFile:566)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at android.content.ContentProvider.update(ContentProvider.java:2074)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at android.content.ContentProvider$Transport.update(ContentProvider.java:578)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:243)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at android.os.Binder.execTransactInternal(Binder.java:1409)
04-30 16:28:34.834 27079 27088 E AnkiDroid: at android.os.Binder.execTransact(Binder.java:1353)
```

### (Optional) Anything else you want to share?

I can contribute to this one

### Research

- [x] I have checked the [manual](https://ankidroid.org/docs/manual.html) and the [FAQ](https://github.com/ankidroid/Anki-Android/wiki/FAQ) and could not find a solution to my issue
- [x] (Optional) I have confirmed the issue is not resolved in the latest alpha release ([instructions](https://docs.ankidroid.org/manual.html#betaTesting))

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.