Search in sources :

Example 26 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundUpdateNote.

private TaskData doInBackgroundUpdateNote(TaskData param) {
    Timber.d("doInBackgroundUpdateNote");
    // Save the note
    Collection col = getCol();
    AbstractSched sched = col.getSched();
    Card editCard = param.getCard();
    Note editNote = editCard.note();
    boolean fromReviewer = param.getBoolean();
    try {
        col.getDb().getDatabase().beginTransaction();
        try {
            // TODO: undo integration
            editNote.flush();
            // flush card too, in case, did has been changed
            editCard.flush();
            if (fromReviewer) {
                Card newCard;
                if (col.getDecks().active().contains(editCard.getDid())) {
                    newCard = editCard;
                    newCard.load();
                    // reload qa-cache
                    newCard.q(true);
                } else {
                    newCard = sched.getCard();
                }
                publishProgress(new TaskData(newCard));
            } else {
                publishProgress(new TaskData(editCard, editNote.stringTags()));
            }
            col.getDb().getDatabase().setTransactionSuccessful();
        } finally {
            col.getDb().getDatabase().endTransaction();
        }
    } catch (RuntimeException e) {
        Timber.e(e, "doInBackgroundUpdateNote - RuntimeException on updating note");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundUpdateNote");
        return new TaskData(false);
    }
    return new TaskData(true);
}
Also used : AbstractSched(com.ichi2.libanki.sched.AbstractSched) Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) Card(com.ichi2.libanki.Card)

Example 27 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method nonTaskUndo.

@VisibleForTesting
public static Card nonTaskUndo(Collection col) {
    AbstractSched sched = col.getSched();
    Card card = col.undo();
    if (card == null) {
        /* multi-card action undone, no action to take here */
        Timber.d("Multi-select undo succeeded");
    } else {
        // cid is actually a card id.
        // a review was undone,
        /* card review undone, set up to review that card again */
        Timber.d("Single card review undo succeeded");
        card.startTimer();
        col.reset();
        sched.deferReset(card);
    }
    return card;
}
Also used : AbstractSched(com.ichi2.libanki.sched.AbstractSched) Card(com.ichi2.libanki.Card) VisibleForTesting(androidx.annotation.VisibleForTesting)

Example 28 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundDismissNotes.

private TaskData doInBackgroundDismissNotes(TaskData param) {
    Collection col = getCol();
    AbstractSched sched = col.getSched();
    Object[] data = param.getObjArray();
    long[] cardIds = (long[]) data[0];
    // query cards
    Card[] cards = new Card[cardIds.length];
    for (int i = 0; i < cardIds.length; i++) {
        cards[i] = col.getCard(cardIds[i]);
    }
    Collection.DismissType type = (Collection.DismissType) data[1];
    try {
        col.getDb().getDatabase().beginTransaction();
        try {
            switch(type) {
                case SUSPEND_CARD_MULTI:
                    {
                        // collect undo information
                        long[] cids = new long[cards.length];
                        boolean[] originalSuspended = new boolean[cards.length];
                        boolean hasUnsuspended = false;
                        for (int i = 0; i < cards.length; i++) {
                            Card card = cards[i];
                            cids[i] = card.getId();
                            if (card.getQueue() != Consts.QUEUE_TYPE_SUSPENDED) {
                                hasUnsuspended = true;
                                originalSuspended[i] = false;
                            } else {
                                originalSuspended[i] = true;
                            }
                        }
                        // otherwise unsuspend all
                        if (hasUnsuspended) {
                            sched.suspendCards(cids);
                        } else {
                            sched.unsuspendCards(cids);
                        }
                        Undoable suspendCardMulti = new UndoSuspendCardMulti(cards, originalSuspended);
                        // mark undo for all at once
                        col.markUndo(suspendCardMulti);
                        // reload cards because they'll be passed back to caller
                        for (Card c : cards) {
                            c.load();
                        }
                        sched.deferReset();
                        break;
                    }
                case FLAG:
                    {
                        int flag = (Integer) data[2];
                        col.setUserFlag(flag, cardIds);
                        for (Card c : cards) {
                            c.load();
                        }
                        break;
                    }
                case MARK_NOTE_MULTI:
                    {
                        Set<Note> notes = CardUtils.getNotes(Arrays.asList(cards));
                        // collect undo information
                        List<Note> originalMarked = new ArrayList<>();
                        List<Note> originalUnmarked = new ArrayList<>();
                        for (Note n : notes) {
                            if (n.hasTag("marked")) {
                                originalMarked.add(n);
                            } else {
                                originalUnmarked.add(n);
                            }
                        }
                        CardUtils.markAll(new ArrayList<>(notes), !originalUnmarked.isEmpty());
                        Undoable markNoteMulti = new UndoMarkNoteMulti(originalMarked, originalUnmarked);
                        // mark undo for all at once
                        col.markUndo(markNoteMulti);
                        // reload cards because they'll be passed back to caller
                        for (Card c : cards) {
                            c.load();
                        }
                        break;
                    }
                case DELETE_NOTE_MULTI:
                    {
                        // list of all ids to pass to remNotes method.
                        // Need Set (-> unique) so we don't pass duplicates to col.remNotes()
                        Set<Note> notes = CardUtils.getNotes(Arrays.asList(cards));
                        List<Card> allCards = CardUtils.getAllCards(notes);
                        // delete note
                        long[] uniqueNoteIds = new long[notes.size()];
                        Note[] notesArr = notes.toArray(new Note[notes.size()]);
                        int count = 0;
                        for (Note note : notes) {
                            uniqueNoteIds[count] = note.getId();
                            count++;
                        }
                        Undoable deleteNoteMulti = new UndoDeleteNoteMulti(notesArr, allCards);
                        col.markUndo(deleteNoteMulti);
                        col.remNotes(uniqueNoteIds);
                        sched.deferReset();
                        // pass back all cards because they can't be retrieved anymore by the caller (since the note is deleted)
                        publishProgress(new TaskData(allCards.toArray(new Card[allCards.size()])));
                        break;
                    }
                case CHANGE_DECK_MULTI:
                    {
                        long newDid = (long) data[2];
                        Timber.i("Changing %d cards to deck: '%d'", cards.length, newDid);
                        Deck deckData = col.getDecks().get(newDid);
                        if (Decks.isDynamic(deckData)) {
                            // #5932 - can't change to a dynamic deck. Use "Rebuild"
                            Timber.w("Attempted to move to dynamic deck. Cancelling task.");
                            return new TaskData(false);
                        }
                        // Confirm that the deck exists (and is not the default)
                        try {
                            long actualId = deckData.getLong("id");
                            if (actualId != newDid) {
                                Timber.w("Attempted to move to deck %d, but got %d", newDid, actualId);
                                return new TaskData(false);
                            }
                        } catch (Exception e) {
                            Timber.e(e, "failed to check deck");
                            return new TaskData(false);
                        }
                        long[] changedCardIds = new long[cards.length];
                        for (int i = 0; i < cards.length; i++) {
                            changedCardIds[i] = cards[i].getId();
                        }
                        col.getSched().remFromDyn(changedCardIds);
                        long[] originalDids = new long[cards.length];
                        for (int i = 0; i < cards.length; i++) {
                            Card card = cards[i];
                            card.load();
                            // save original did for undo
                            originalDids[i] = card.getDid();
                            // then set the card ID to the new deck
                            card.setDid(newDid);
                            Note note = card.note();
                            note.flush();
                            // flush card too, in case, did has been changed
                            card.flush();
                        }
                        Undoable changeDeckMulti = new UndoChangeDeckMulti(cards, originalDids);
                        // mark undo for all at once
                        col.markUndo(changeDeckMulti);
                        break;
                    }
                case RESCHEDULE_CARDS:
                case REPOSITION_CARDS:
                case RESET_CARDS:
                    {
                        // collect undo information, sensitive to memory pressure, same for all 3 cases
                        try {
                            Timber.d("Saving undo information of type %s on %d cards", type, cards.length);
                            Card[] cards_copied = deepCopyCardArray(cards);
                            Undoable repositionRescheduleResetCards = new UndoRepositionRescheduleResetCards(type, cards_copied);
                            col.markUndo(repositionRescheduleResetCards);
                        } catch (CancellationException ce) {
                            Timber.i(ce, "Cancelled while handling type %s, skipping undo", type);
                        }
                        switch(type) {
                            case RESCHEDULE_CARDS:
                                sched.reschedCards(cardIds, (Integer) data[2], (Integer) data[2]);
                                break;
                            case REPOSITION_CARDS:
                                sched.sortCards(cardIds, (Integer) data[2], 1, false, true);
                                break;
                            case RESET_CARDS:
                                sched.forgetCards(cardIds);
                                break;
                        }
                        // In all cases schedule a new card so Reviewer doesn't sit on the old one
                        col.reset();
                        publishProgress(new TaskData(sched.getCard(), 0));
                        break;
                    }
            }
            col.getDb().getDatabase().setTransactionSuccessful();
        } finally {
            col.getDb().getDatabase().endTransaction();
        }
    } catch (RuntimeException e) {
        Timber.e(e, "doInBackgroundSuspendCard - RuntimeException on suspending card");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundSuspendCard");
        return new TaskData(false);
    }
    // (querying the cards again is unnecessarily expensive)
    return new TaskData(true, cards);
}
Also used : Undoable(com.ichi2.libanki.Undoable) Set(java.util.Set) AbstractSched(com.ichi2.libanki.sched.AbstractSched) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) Deck(com.ichi2.libanki.Deck) JSONException(com.ichi2.utils.JSONException) CancellationException(java.util.concurrent.CancellationException) FileNotFoundException(java.io.FileNotFoundException) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) ImportExportException(com.ichi2.anki.exception.ImportExportException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) Card(com.ichi2.libanki.Card) CancellationException(java.util.concurrent.CancellationException) Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) JSONObject(com.ichi2.utils.JSONObject)

Example 29 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundExportApkg.

private TaskData doInBackgroundExportApkg(TaskData param) {
    Timber.d("doInBackgroundExportApkg");
    Object[] data = param.getObjArray();
    Collection col = (Collection) data[0];
    String apkgPath = (String) data[1];
    Long did = (Long) data[2];
    boolean includeSched = (Boolean) data[3];
    boolean includeMedia = (Boolean) data[4];
    boolean exportApkg = (Boolean) data[5];
    boolean exportCard = (Boolean) data[6];
    try {
        AnkiPackageExporter exporter = new AnkiPackageExporter(col);
        exporter.setIncludeSched(includeSched);
        exporter.setIncludeMedia(includeMedia);
        exporter.setExportCard(exportCard);
        exporter.setExportApkg(exportApkg);
        exporter.setDid(did);
        exporter.exportInto(apkgPath, mContext);
    } catch (FileNotFoundException e) {
        Timber.e(e, "FileNotFoundException in doInBackgroundExportApkg");
        return new TaskData(false);
    } catch (IOException e) {
        Timber.e(e, "IOException in doInBackgroundExportApkg");
        return new TaskData(false);
    } catch (JSONException e) {
        Timber.e(e, "JSOnException in doInBackgroundExportApkg");
        return new TaskData(false);
    } catch (ImportExportException e) {
        Timber.e(e, "ImportExportException in doInBackgroundExportApkg");
        return new TaskData(e.getMessage(), true);
    }
    return new TaskData(exportCard ? apkgPath.replace(".apkg", ".card") : apkgPath);
}
Also used : FileNotFoundException(java.io.FileNotFoundException) JSONException(com.ichi2.utils.JSONException) IOException(java.io.IOException) AnkiPackageExporter(com.ichi2.libanki.AnkiPackageExporter) ImportExportException(com.ichi2.anki.exception.ImportExportException) Collection(com.ichi2.libanki.Collection) JSONObject(com.ichi2.utils.JSONObject)

Example 30 with CARD

use of com.ichi2.anki.CardBrowser.Column.CARD in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method deepCopyCardArray.

private Card[] deepCopyCardArray(Card[] originals) throws CancellationException {
    Collection col = CollectionHelper.getInstance().getCol(AnkiDroidApp.getInstance());
    Card[] copies = new Card[originals.length];
    for (int i = 0; i < originals.length; i++) {
        if (isCancelled()) {
            Timber.i("Cancelled during deep copy, probably memory pressure?");
            throw new CancellationException("Cancelled during deep copy");
        }
        // TODO: the performance-naive implementation loads from database instead of working in memory
        // the high performance version would implement .clone() on Card and test it well
        copies[i] = new Card(col, originals[i].getId());
    }
    return copies;
}
Also used : CancellationException(java.util.concurrent.CancellationException) Collection(com.ichi2.libanki.Collection) Card(com.ichi2.libanki.Card)

Aggregations

Card (com.ichi2.libanki.Card)222 Test (org.junit.Test)212 Collection (com.ichi2.libanki.Collection)179 Note (com.ichi2.libanki.Note)173 RobolectricTest (com.ichi2.anki.RobolectricTest)168 JSONObject (com.ichi2.utils.JSONObject)114 JSONArray (com.ichi2.utils.JSONArray)80 DeckConfig (com.ichi2.libanki.DeckConfig)72 ArrayList (java.util.ArrayList)48 NonNull (androidx.annotation.NonNull)33 Deck (com.ichi2.libanki.Deck)32 Intent (android.content.Intent)29 JSONException (com.ichi2.utils.JSONException)27 Model (com.ichi2.libanki.Model)26 Context (android.content.Context)23 SuppressLint (android.annotation.SuppressLint)22 Cursor (android.database.Cursor)22 HashMap (java.util.HashMap)21 Matchers.containsString (org.hamcrest.Matchers.containsString)20 List (java.util.List)17