Search in sources :

Example 1 with Undoable

use of com.ichi2.libanki.Undoable 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 2 with Undoable

use of com.ichi2.libanki.Undoable in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundResetDeck.

private TaskData doInBackgroundResetDeck(TaskData param) {
    Timber.d("doInBackgroundSearchCardIds");
    Collection col = getCol();
    String query = (String) param.getObjArray()[0];
    if (isCancelled()) {
        Timber.d("doInBackgroundSearchCards was cancelled so return null");
        return null;
    }
    List<Long> searchResult_ = col.findCards(query, true, this);
    long[] cardIds = new long[searchResult_.size()];
    Card[] cards = new Card[searchResult_.size()];
    for (int i = 0; i < searchResult_.size(); i++) {
        cardIds[i] = searchResult_.get(i);
        cards[i] = col.getCard(searchResult_.get(i));
    }
    try {
        col.getDb().getDatabase().beginTransaction();
        try {
            try {
                Timber.d("Saving undo information of type %s on %d cards", RESET_CARDS, cards.length);
                Card[] cards_copied = deepCopyCardArray(cards);
                Undoable repositionRescheduleResetCards = new UndoRepositionRescheduleResetCards(RESET_CARDS, cards_copied);
                col.markUndo(repositionRescheduleResetCards);
            } catch (CancellationException ce) {
                Timber.i(ce, "Cancelled while handling type %s, skipping undo", RESET_CARDS);
            }
            AbstractSched sched = col.getSched();
            sched.forgetCards(cardIds);
            col.reset();
            publishProgress(new TaskData(sched.getCard(), 0));
            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);
    }
    return new TaskData(true, cards);
}
Also used : Undoable(com.ichi2.libanki.Undoable) AbstractSched(com.ichi2.libanki.sched.AbstractSched) Card(com.ichi2.libanki.Card) CancellationException(java.util.concurrent.CancellationException) Collection(com.ichi2.libanki.Collection)

Example 3 with Undoable

use of com.ichi2.libanki.Undoable in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundDismissNote.

private TaskData doInBackgroundDismissNote(TaskData param) {
    Collection col = getCol();
    AbstractSched sched = col.getSched();
    Object[] data = param.getObjArray();
    Card card = (Card) data[0];
    Collection.DismissType type = (Collection.DismissType) data[1];
    Note note = card.note();
    try {
        col.getDb().getDatabase().beginTransaction();
        try {
            sched.deferReset();
            switch(type) {
                case BURY_CARD:
                    // collect undo information
                    Undoable buryCard = revertToProvidedState(BURY_CARD, card);
                    col.markUndo(buryCard);
                    // then bury
                    sched.buryCards(new long[] { card.getId() });
                    break;
                case BURY_NOTE:
                    // collect undo information
                    Undoable buryNote = revertToProvidedState(BURY_NOTE, card);
                    col.markUndo(buryNote);
                    // then bury
                    sched.buryNote(note.getId());
                    break;
                case SUSPEND_CARD:
                    // collect undo information
                    Card suspendedCard = card.clone();
                    Undoable suspendCard = new UndoSuspendCard(suspendedCard);
                    col.markUndo(suspendCard);
                    // suspend card
                    if (card.getQueue() == Consts.QUEUE_TYPE_SUSPENDED) {
                        sched.unsuspendCards(new long[] { card.getId() });
                    } else {
                        sched.suspendCards(new long[] { card.getId() });
                    }
                    break;
                case SUSPEND_NOTE:
                    {
                        // collect undo information
                        ArrayList<Card> cards = note.cards();
                        long[] cids = new long[cards.size()];
                        for (int i = 0; i < cards.size(); i++) {
                            cids[i] = cards.get(i).getId();
                        }
                        col.markUndo(revertToProvidedState(SUSPEND_NOTE, card));
                        // suspend note
                        sched.suspendCards(cids);
                        break;
                    }
                case DELETE_NOTE:
                    {
                        // collect undo information
                        ArrayList<Card> allCs = note.cards();
                        Undoable deleteNote = new UndoDeleteNote(note, allCs, card);
                        col.markUndo(deleteNote);
                        // delete note
                        col.remNotes(new long[] { note.getId() });
                        break;
                    }
            }
            // With sHadCardQueue set, getCard() resets the scheduler prior to getting the next card
            publishProgress(new TaskData(col.getSched().getCard(), 0));
            col.getDb().getDatabase().setTransactionSuccessful();
        } finally {
            col.getDb().getDatabase().endTransaction();
        }
    } catch (RuntimeException e) {
        Timber.e(e, "doInBackgroundDismissNote - RuntimeException on dismissing note, dismiss type %s", type);
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundDismissNote");
        return new TaskData(false);
    }
    return new TaskData(true);
}
Also used : Undoable(com.ichi2.libanki.Undoable) AbstractSched(com.ichi2.libanki.sched.AbstractSched) ArrayList(java.util.ArrayList) Card(com.ichi2.libanki.Card) Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) JSONObject(com.ichi2.utils.JSONObject)

Aggregations

Card (com.ichi2.libanki.Card)3 Collection (com.ichi2.libanki.Collection)3 Undoable (com.ichi2.libanki.Undoable)3 AbstractSched (com.ichi2.libanki.sched.AbstractSched)3 Note (com.ichi2.libanki.Note)2 JSONObject (com.ichi2.utils.JSONObject)2 ArrayList (java.util.ArrayList)2 CancellationException (java.util.concurrent.CancellationException)2 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)1 ImportExportException (com.ichi2.anki.exception.ImportExportException)1 Deck (com.ichi2.libanki.Deck)1 JSONException (com.ichi2.utils.JSONException)1 FileNotFoundException (java.io.FileNotFoundException)1 IOException (java.io.IOException)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 Set (java.util.Set)1 ExecutionException (java.util.concurrent.ExecutionException)1