Search in sources :

Example 1 with Reviewer

use of com.ichi2.anki.Reviewer in project AnkiChinaAndroid by ankichinateam.

the class CardBrowser method onActivityResult.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // FIXME:
    Timber.d("onActivityResult(requestCode=%d, resultCode=%d)", requestCode, resultCode);
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == DeckPicker.RESULT_DB_ERROR) {
        closeCardBrowser(DeckPicker.RESULT_DB_ERROR);
    }
    if (requestCode == EDIT_CARD && resultCode != RESULT_CANCELED) {
        Timber.i("CardBrowser:: CardBrowser: Saving card...");
        CollectionTask.launchCollectionTask(UPDATE_NOTE, updateCardHandler(), new TaskData(sCardBrowserCard, false));
    } else if (requestCode == ADD_NOTE && resultCode == RESULT_OK) {
        if (mSearchView != null) {
            mSearchTerms = mSearchView.getQuery().toString();
            searchCards();
        } else {
            Timber.w("Note was added from browser and on return mSearchView == null");
        }
    }
    // Previewing can now perform an "edit", so it can pass on a reloadRequired
    if (requestCode == PREVIEW_CARDS && data != null && (data.getBooleanExtra("reloadRequired", false) || data.getBooleanExtra("noteChanged", false))) {
        searchCards();
        if (getReviewerCardId() == mCurrentCardId) {
            mReloadRequired = true;
        }
    }
    if (requestCode == EDIT_CARD && data != null && (data.getBooleanExtra("reloadRequired", false) || data.getBooleanExtra("noteChanged", false))) {
        // if reloadRequired or noteChanged flag was sent from note editor then reload card list
        searchCards();
        // in use by reviewer?
        if (getReviewerCardId() == mCurrentCardId) {
            mReloadRequired = true;
        }
    }
    // maybe the availability of undo changed
    invalidateOptionsMenu();
}
Also used : TaskData(com.ichi2.async.TaskData)

Example 2 with Reviewer

use of com.ichi2.anki.Reviewer in project AnkiChinaAndroid by ankichinateam.

the class CustomStudyDialog method buildInputDialog.

/**
 * Build an input dialog that is used to get a parameter related to custom study from the user
 *
 * @param dialogId
 * @return
 */
private MaterialDialog buildInputDialog(final int dialogId) {
    /*
            TODO: Try to change to a standard input dialog (currently the thing holding us back is having the extra
            TODO: hint line for the number of cards available, and having the pre-filled text selected by default)
        */
    // Input dialogs
    Resources res = getActivity().getResources();
    // Show input dialog for an individual custom study dialog
    View v = getActivity().getLayoutInflater().inflate(R.layout.styled_custom_study_details_dialog, null);
    TextView textView1 = (TextView) v.findViewById(R.id.custom_study_details_text1);
    TextView textView2 = (TextView) v.findViewById(R.id.custom_study_details_text2);
    final EditText mEditText = (EditText) v.findViewById(R.id.custom_study_details_edittext2);
    // Set the text
    textView1.setText(getText1());
    textView2.setText(getText2());
    mEditText.setText(getDefaultValue());
    // Give EditText focus and show keyboard
    mEditText.setSelectAllOnFocus(true);
    mEditText.requestFocus();
    if (dialogId == CUSTOM_STUDY_NEW || dialogId == CUSTOM_STUDY_REV) {
        mEditText.setInputType(EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_FLAG_SIGNED);
    }
    // deck id
    final long did = getArguments().getLong("did");
    // Whether or not to jump straight to the reviewer
    final boolean jumpToReviewer = getArguments().getBoolean("jumpToReviewer");
    // Set builder parameters
    MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity()).customView(v, true).positiveText(res.getString(R.string.dialog_ok)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((dialog, which) -> {
        Collection col = CollectionHelper.getInstance().getCol(getActivity());
        // Get the value selected by user
        int n;
        try {
            n = Integer.parseInt(mEditText.getText().toString());
        } catch (Exception ignored) {
            // This should never happen because we disable positive button for non-parsable inputs
            return;
        }
        // Set behavior when clicking OK button
        switch(dialogId) {
            case CUSTOM_STUDY_NEW:
                {
                    AnkiDroidApp.getSharedPrefs(getActivity()).edit().putInt("extendNew", n).commit();
                    Deck deck = col.getDecks().get(did);
                    deck.put("extendNew", n);
                    col.getDecks().save(deck);
                    col.getSched().extendLimits(n, 0);
                    onLimitsExtended(jumpToReviewer);
                    break;
                }
            case CUSTOM_STUDY_REV:
                {
                    AnkiDroidApp.getSharedPrefs(getActivity()).edit().putInt("extendRev", n).commit();
                    Deck deck = col.getDecks().get(did);
                    deck.put("extendRev", n);
                    col.getDecks().save(deck);
                    col.getSched().extendLimits(0, n);
                    onLimitsExtended(jumpToReviewer);
                    break;
                }
            case CUSTOM_STUDY_FORGOT:
                {
                    JSONArray ar = new JSONArray();
                    ar.put(0, 1);
                    createCustomStudySession(ar, new Object[] { String.format(Locale.US, "rated:%d:1", n), Consts.DYN_MAX_SIZE, Consts.DYN_RANDOM }, false);
                    break;
                }
            case CUSTOM_STUDY_AHEAD:
                {
                    createCustomStudySession(new JSONArray(), new Object[] { String.format(Locale.US, "prop:due<=%d", n), Consts.DYN_MAX_SIZE, Consts.DYN_DUE }, true);
                    break;
                }
            case CUSTOM_STUDY_RANDOM:
                {
                    createCustomStudySession(new JSONArray(), new Object[] { "", n, Consts.DYN_RANDOM }, true);
                    break;
                }
            case CUSTOM_STUDY_PREVIEW:
                {
                    createCustomStudySession(new JSONArray(), new Object[] { "is:new added:" + Integer.toString(n), Consts.DYN_MAX_SIZE, Consts.DYN_OLDEST }, false);
                    break;
                }
            default:
                break;
        }
    }).onNegative((dialog, which) -> getAnkiActivity().dismissAllDialogFragments());
    final MaterialDialog dialog = builder.build();
    mEditText.addTextChangedListener(new TextWatcher() {

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }

        @Override
        public void afterTextChanged(Editable editable) {
            try {
                Integer.parseInt(mEditText.getText().toString());
                dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
            } catch (Exception ignored) {
                dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
            }
        }
    });
    // Show soft keyboard
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    return dialog;
}
Also used : EditText(android.widget.EditText) TaskListener(com.ichi2.async.TaskListener) Bundle(android.os.Bundle) Deck(com.ichi2.libanki.Deck) NonNull(androidx.annotation.NonNull) AnalyticsDialogFragment(com.ichi2.anki.analytics.AnalyticsDialogFragment) WindowManager(android.view.WindowManager) R(com.ichi2.anki.R) Dialog(android.app.Dialog) Intent(android.content.Intent) HashMap(java.util.HashMap) Collection(com.ichi2.libanki.Collection) Editable(android.text.Editable) ArrayList(java.util.ArrayList) TASK_TYPE(com.ichi2.async.CollectionTask.TASK_TYPE) JSONArray(com.ichi2.utils.JSONArray) Locale(java.util.Locale) UIUtils(com.ichi2.anki.UIUtils) View(android.view.View) TaskData(com.ichi2.async.TaskData) DeckPicker(com.ichi2.anki.DeckPicker) TextUtils(android.text.TextUtils) DialogAction(com.afollestad.materialdialogs.DialogAction) CollectionTask(com.ichi2.async.CollectionTask) JSONObject(com.ichi2.utils.JSONObject) Timber(timber.log.Timber) CollectionHelper(com.ichi2.anki.CollectionHelper) AnkiActivity(com.ichi2.anki.AnkiActivity) List(java.util.List) TextView(android.widget.TextView) SharedPreferences(android.content.SharedPreferences) AnkiDroidApp(com.ichi2.anki.AnkiDroidApp) Consts(com.ichi2.libanki.Consts) Reviewer(com.ichi2.anki.Reviewer) TaskListenerWithContext(com.ichi2.async.TaskListenerWithContext) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) VisibleForTesting(androidx.annotation.VisibleForTesting) EditorInfo(android.view.inputmethod.EditorInfo) DeckOptions(com.ichi2.anki.DeckOptions) EditText(android.widget.EditText) Resources(android.content.res.Resources) TextWatcher(android.text.TextWatcher) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) JSONArray(com.ichi2.utils.JSONArray) Deck(com.ichi2.libanki.Deck) View(android.view.View) TextView(android.widget.TextView) Collection(com.ichi2.libanki.Collection) TextWatcher(android.text.TextWatcher) Editable(android.text.Editable) TextView(android.widget.TextView) Resources(android.content.res.Resources)

Example 3 with Reviewer

use of com.ichi2.anki.Reviewer in project AnkiChinaAndroid by ankichinateam.

the class SelfStudyActivity method onActivityResult.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // FIXME:
    Timber.d("onActivityResult(requestCode=%d, resultCode=%d)", requestCode, resultCode);
    if (data != null) {
        Timber.d("onActivityResult data (reloadRequired=%s, noteChanged=%s)", data.getBooleanExtra("reloadRequired", false), data.getBooleanExtra("noteChanged", false));
    }
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == DeckPicker.RESULT_DB_ERROR) {
        closeCardBrowser(DeckPicker.RESULT_DB_ERROR);
    }
    if (requestCode == EDIT_CARD && resultCode != RESULT_CANCELED) {
        Timber.i("CardBrowser:: CardBrowser: Saving card...");
        CollectionTask.launchCollectionTask(UPDATE_NOTE, updateCardHandler(), new TaskData(sCardBrowserCard, false));
    } else if (requestCode == ADD_NOTE && resultCode == RESULT_OK) {
        if (mSearchView != null) {
            mSearchTerms = mSearchView.getQuery().toString();
            searchCards();
        } else {
            Timber.w("Note was added from browser and on return mSearchView == null");
        }
    }
    // }
    if (requestCode == PREVIEW_CARDS && data != null && (data.getBooleanExtra("reloadRequired", false) || data.getBooleanExtra("noteChanged", false))) {
        searchCards();
        if (getReviewerCardId() == mCurrentCardId) {
            mReloadRequired = true;
        }
    }
    if (requestCode == EDIT_CARD && data != null && (data.getBooleanExtra("reloadRequired", false) || data.getBooleanExtra("noteChanged", false))) {
        // if reloadRequired or noteChanged flag was sent from note editor then reload card list
        searchCards();
        // in use by reviewer?
        if (getReviewerCardId() == mCurrentCardId) {
            mReloadRequired = true;
        }
    }
    // maybe the availability of undo changed
    invalidateOptionsMenu();
}
Also used : TaskData(com.ichi2.async.TaskData)

Example 4 with Reviewer

use of com.ichi2.anki.Reviewer 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 5 with Reviewer

use of com.ichi2.anki.Reviewer in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method currentCardIsInQueueWithDeck.

protected boolean currentCardIsInQueueWithDeck(@Consts.CARD_QUEUE int queue, long did) {
    // mCurrentCard may be set to null when the reviewer gets closed. So we copy it to be sure to avoid NullPointerException
    Card currentCard = mCurrentCard;
    List<Long> currentCardParentsDid = mCurrentCardParentsDid;
    return currentCard != null && currentCard.getQueue() == queue && currentCardParentsDid != null && currentCardParentsDid.contains(did);
}
Also used : Card(com.ichi2.libanki.Card)

Aggregations

Test (org.junit.Test)11 Collection (com.ichi2.libanki.Collection)10 Card (com.ichi2.libanki.Card)8 JSONObject (com.ichi2.utils.JSONObject)5 Intent (android.content.Intent)4 NonNull (androidx.annotation.NonNull)4 Nullable (androidx.annotation.Nullable)4 Model (com.ichi2.libanki.Model)4 Note (com.ichi2.libanki.Note)4 JSONArray (com.ichi2.utils.JSONArray)4 Resources (android.content.res.Resources)3 View (android.view.View)3 WindowManager (android.view.WindowManager)3 VisibleForTesting (androidx.annotation.VisibleForTesting)3 MaterialDialog (com.afollestad.materialdialogs.MaterialDialog)3 TaskData (com.ichi2.async.TaskData)3 ArrayList (java.util.ArrayList)3 Context (android.content.Context)2 SharedPreferences (android.content.SharedPreferences)2 Uri (android.net.Uri)2