/** Return one card starting from the given cursor. */ private static Card oneFromCursor(Cursor cursor) { if (cursor.isClosed()) { throw new SQLException(); } cursor.moveToFirst(); Card card = new Card(); card.id = cursor.getLong(0); card.interval = cursor.getDouble(1); card.question = cursor.getString(2); card.answer = cursor.getString(3); return card; }
public void answerCard(Card card, int ease) { Cursor cursor = null; String undoName = "Answer Card"; setUndoStart(undoName); double now = System.currentTimeMillis() / 1000.0; // Old state String oldState = cardState(card); double lastDelaySecs = System.currentTimeMillis() / 1000.0 - card.combinedDue; double lastDelay = lastDelaySecs / 86400.0; int oldSuc = card.successive; // update card details double last = card.interval; card.interval = nextInterval(card, ease); if (lastDelay >= 0) card.lastInterval = last; // keep last interval if reviewing early if (card.reps != 0) card.lastDue = card.due; // only update if card was not new card.due = nextDue(card, ease, oldState); card.isDue = 0; card.lastFactor = card.factor; if (lastDelay >= 0) updateFactor(card, ease); // don't update factor if learning ahead // spacing double space, spaceFactor, minSpacing, minOfOtherCards; try { cursor = AnkiDb.database.rawQuery( "SELECT models.initialSpacing, models.spacing " + "FROM facts, models " + "WHERE facts.modelId = models.id and " + "facts.id = " + card.factId, null); if (!cursor.moveToFirst()) { minSpacing = 0; spaceFactor = 0; } else { minSpacing = cursor.getDouble(0); spaceFactor = cursor.getDouble(1); } } finally { if (cursor != null) cursor.close(); } try { cursor = AnkiDb.database.rawQuery( "SELECT min(interval) " + "FROM cards " + "WHERE factId = " + card.factId + " and id != " + card.id, null); if (!cursor.moveToFirst()) minOfOtherCards = 0; else minOfOtherCards = cursor.getDouble(0); } finally { if (cursor != null) cursor.close(); } if (minOfOtherCards != 0) space = Math.min(minOfOtherCards, card.interval); else space = 0; space = space * spaceFactor * 86400.0; space = Math.max(minSpacing, space); space += System.currentTimeMillis() / 1000.0; card.combinedDue = Math.max(card.due, space); // check what other cards we've spaced String extra; if (this.reviewEarly) extra = ""; else { // if not reviewing early, make sure the current card is counted // even if it was not due yet (it's a failed card) extra = "or id = " + card.id; } try { cursor = AnkiDb.database.rawQuery( "SELECT type, count(type) " + "FROM cards " + "WHERE factId = " + card.factId + " and " + "(isDue = 1 " + extra + ") " + "GROUP BY type", null); while (cursor.moveToNext()) { if (cursor.getInt(0) == 0) failedSoonCount -= cursor.getInt(1); else if (cursor.getInt(0) == 1) revCount -= cursor.getInt(1); else newCount -= cursor.getInt(1); } } finally { if (cursor != null) cursor.close(); } // space other cards AnkiDb.database.execSQL( String.format( ENGLISH_LOCALE, "UPDATE cards " + "SET spaceUntil = %f, " + "combinedDue = max(%f, due), " + "modified = %f, " + "isDue = 0 " + "WHERE id != %d and factId = %d", space, space, now, card.id, card.factId)); card.spaceUntil = 0; // temp suspend if learning ahead if (reviewEarly && lastDelay < 0) if (oldSuc != 0 || lastDelaySecs > delay0 || !showFailedLast()) card.priority = -1; // card stats card.updateStats(ease, oldState); card.toDB(); // global/daily stats Stats.updateAllStats(this.globalStats, this.dailyStats, card, ease, oldState); // review history CardHistoryEntry entry = new CardHistoryEntry(card, ease, lastDelay); entry.writeSQL(); modified = now; // // TODO: Fix leech handling // if (isLeech(card)) // card = handleLeech(card); setUndoEnd(undoName); }