use of com.ichi2.async.CollectionTask.TASK_TYPE.UNDO 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);
}
use of com.ichi2.async.CollectionTask.TASK_TYPE.UNDO 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;
}
use of com.ichi2.async.CollectionTask.TASK_TYPE.UNDO 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);
}
use of com.ichi2.async.CollectionTask.TASK_TYPE.UNDO 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);
}
use of com.ichi2.async.CollectionTask.TASK_TYPE.UNDO in project AnkiChinaAndroid by ankichinateam.
the class CollectionTask method doInBackgroundSaveModel.
/**
* Handles everything for a model change at once - template add / deletes as well as content updates
*/
private TaskData doInBackgroundSaveModel(TaskData param) {
Timber.d("doInBackgroundSaveModel");
Collection col = getCol();
Object[] args = param.getObjArray();
Model model = (Model) args[0];
ArrayList<Object[]> templateChanges = (ArrayList<Object[]>) args[1];
Model oldModel = col.getModels().get(model.getLong("id"));
// TODO need to save all the cards that will go away, for undo
// (do I need to remove them from graves during undo also?)
// - undo (except for cards) could just be Models.update(model) / Models.flush() / Collection.reset() (that was prior "undo")
JSONArray newTemplates = model.getJSONArray("tmpls");
col.getDb().getDatabase().beginTransaction();
try {
for (Object[] change : templateChanges) {
JSONArray oldTemplates = oldModel.getJSONArray("tmpls");
switch((TemporaryModel.ChangeType) change[1]) {
case ADD:
Timber.d("doInBackgroundSaveModel() adding template %s", change[0]);
try {
col.getModels().addTemplate(oldModel, newTemplates.getJSONObject((int) change[0]));
} catch (Exception e) {
Timber.e(e, "Unable to add template %s to model %s", change[0], model.getLong("id"));
return new TaskData(e.getLocalizedMessage(), false);
}
break;
case DELETE:
Timber.d("doInBackgroundSaveModel() deleting template currently at ordinal %s", change[0]);
try {
col.getModels().remTemplate(oldModel, oldTemplates.getJSONObject((int) change[0]));
} catch (Exception e) {
Timber.e(e, "Unable to delete template %s from model %s", change[0], model.getLong("id"));
return new TaskData(e.getLocalizedMessage(), false);
}
break;
default:
Timber.w("Unknown change type? %s", change[1]);
break;
}
}
col.getModels().save(model, true);
col.getModels().update(model);
col.reset();
col.save();
if (col.getDb().getDatabase().inTransaction()) {
col.getDb().getDatabase().setTransactionSuccessful();
} else {
Timber.i("CollectionTask::SaveModel was not in a transaction? Cannot mark transaction successful.");
}
} finally {
if (col.getDb().getDatabase().inTransaction()) {
col.getDb().getDatabase().endTransaction();
} else {
Timber.i("CollectionTask::SaveModel was not in a transaction? Cannot end transaction.");
}
}
return new TaskData(true);
}
Aggregations