Search in sources :

Example 6 with AnswerButtons

use of com.ichi2.anki.reviewer.AnswerButtons in project Anki-Android by ankidroid.

the class AbstractFlashcardViewer method answerCard.

protected void answerCard(@Consts.BUTTON_TYPE int ease) {
    if (mInAnswer) {
        return;
    }
    mIsSelecting = false;
    int buttonNumber = getCol().getSched().answerButtons(mCurrentCard);
    // Detect invalid ease for current card (e.g. by using keyboard shortcut or gesture).
    if (buttonNumber < ease) {
        return;
    }
    // Temporarily sets the answer indicator dots appearing below the toolbar
    mPreviousAnswerIndicator.displayAnswerIndicator(ease, buttonNumber);
    mSoundPlayer.stopSounds();
    mCurrentEase = ease;
    new SchedulerService.AnswerAndGetCard(mCurrentCard, mCurrentEase).runWithHandler(answerCardHandler(true));
}
Also used : SchedulerService(com.ichi2.anki.servicelayer.SchedulerService) SuppressLint(android.annotation.SuppressLint)

Example 7 with AnswerButtons

use of com.ichi2.anki.reviewer.AnswerButtons in project Anki-Android by ankidroid.

the class Reviewer method setFullScreen.

// #9332: UI Visibility -> Insets
@SuppressWarnings("deprecation")
private void setFullScreen(final AbstractFlashcardViewer a) {
    // Set appropriate flags to enable Sticky Immersive mode.
    a.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | // | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // temporarily disabled due to #5245
    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_IMMERSIVE);
    // Show / hide the Action bar together with the status bar
    SharedPreferences prefs = AnkiDroidApp.getSharedPrefs(a);
    FullScreenMode fullscreenMode = FullScreenMode.fromPreference(prefs);
    a.getWindow().setStatusBarColor(Themes.getColorFromAttr(a, R.attr.colorPrimaryDark));
    View decorView = a.getWindow().getDecorView();
    decorView.setOnSystemUiVisibilityChangeListener(flags -> {
        final View toolbar = a.findViewById(R.id.toolbar);
        final View answerButtons = a.findViewById(R.id.answer_options_layout);
        final View topbar = a.findViewById(R.id.top_bar);
        if (toolbar == null || topbar == null || answerButtons == null) {
            return;
        }
        // Note that system bars will only be "visible" if none of the
        // LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
        boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
        Timber.d("System UI visibility change. Visible: %b", visible);
        if (visible) {
            showViewWithAnimation(toolbar);
            if (fullscreenMode.equals(FullScreenMode.FULLSCREEN_ALL_GONE)) {
                showViewWithAnimation(topbar);
                showViewWithAnimation(answerButtons);
            }
        } else {
            hideViewWithAnimation(toolbar);
            if (fullscreenMode.equals(FullScreenMode.FULLSCREEN_ALL_GONE)) {
                hideViewWithAnimation(topbar);
                hideViewWithAnimation(answerButtons);
            }
        }
    });
}
Also used : SharedPreferences(android.content.SharedPreferences) FullScreenMode(com.ichi2.anki.reviewer.FullScreenMode) ImageView(android.widget.ImageView) View(android.view.View) WebView(android.webkit.WebView) TextView(android.widget.TextView) AudioView(com.ichi2.anki.multimediacard.AudioView)

Example 8 with AnswerButtons

use of com.ichi2.anki.reviewer.AnswerButtons in project AnkiChinaAndroid by ankichinateam.

the class CardContentProvider method query.

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String order) {
    if (!hasReadWritePermission() && shouldEnforceQueryOrInsertSecurity()) {
        throwSecurityException("query", uri);
    }
    Collection col = CollectionHelper.getInstance().getCol(mContext);
    if (col == null) {
        throw new IllegalStateException(COL_NULL_ERROR_MSG);
    }
    Timber.d(getLogMessage("query", uri));
    // Find out what data the user is requesting
    int match = sUriMatcher.match(uri);
    switch(match) {
        case NOTES_V2:
            {
                /* Search for notes using direct SQL query */
                String[] proj = sanitizeNoteProjection(projection);
                String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, selection, null, null, order, null);
                return col.getDb().getDatabase().query(sql, selectionArgs);
            }
        case NOTES:
            {
                /* Search for notes using the libanki browser syntax */
                String[] proj = sanitizeNoteProjection(projection);
                String query = (selection != null) ? selection : "";
                List<Long> noteIds = col.findNotes(query);
                if ((noteIds != null) && (!noteIds.isEmpty())) {
                    String sel = String.format("id in (%s)", TextUtils.join(",", noteIds));
                    String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, sel, null, null, order, null);
                    return col.getDb().getDatabase().query(sql);
                } else {
                    return null;
                }
            }
        case NOTES_ID:
            {
                /* Direct access note with specific ID*/
                String noteId = uri.getPathSegments().get(1);
                String[] proj = sanitizeNoteProjection(projection);
                String sql = SQLiteQueryBuilder.buildQueryString(false, "notes", proj, "id=?", null, null, order, null);
                return col.getDb().getDatabase().query(sql, new String[] { noteId });
            }
        case NOTES_ID_CARDS:
            {
                Note currentNote = getNoteFromUri(uri, col);
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Card.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                for (Card currentCard : currentNote.cards()) {
                    addCardToCursor(currentCard, rv, col, columns);
                }
                return rv;
            }
        case NOTES_ID_CARDS_ORD:
            {
                Card currentCard = getCardFromUri(uri, col);
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Card.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                addCardToCursor(currentCard, rv, col, columns);
                return rv;
            }
        case MODELS:
            {
                Models models = col.getModels();
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Model.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                for (Long modelId : models.getModels().keySet()) {
                    addModelToCursor(modelId, models, rv, columns);
                }
                return rv;
            }
        case MODELS_ID:
            {
                long modelId = getModelIdFromUri(uri, col);
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Model.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                addModelToCursor(modelId, col.getModels(), rv, columns);
                return rv;
            }
        case MODELS_ID_TEMPLATES:
            {
                /* Direct access model templates */
                Models models = col.getModels();
                Model currentModel = models.get(getModelIdFromUri(uri, col));
                String[] columns = ((projection != null) ? projection : CardTemplate.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                try {
                    JSONArray templates = currentModel.getJSONArray("tmpls");
                    for (int idx = 0; idx < templates.length(); idx++) {
                        JSONObject template = templates.getJSONObject(idx);
                        addTemplateToCursor(template, currentModel, idx + 1, models, rv, columns);
                    }
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Model is malformed", e);
                }
                return rv;
            }
        case MODELS_ID_TEMPLATES_ID:
            {
                /* Direct access model template with specific ID */
                Models models = col.getModels();
                int ord = Integer.parseInt(uri.getLastPathSegment());
                Model currentModel = models.get(getModelIdFromUri(uri, col));
                String[] columns = ((projection != null) ? projection : CardTemplate.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                try {
                    JSONObject template = getTemplateFromUri(uri, col);
                    addTemplateToCursor(template, currentModel, ord + 1, models, rv, columns);
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Model is malformed", e);
                }
                return rv;
            }
        case SCHEDULE:
            {
                String[] columns = ((projection != null) ? projection : FlashCardsContract.ReviewInfo.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                long selectedDeckBeforeQuery = col.getDecks().selected();
                long deckIdOfTemporarilySelectedDeck = -1;
                // the number of scheduled cards to return
                int limit = 1;
                int selectionArgIndex = 0;
                // parsing the selection arguments
                if (selection != null) {
                    // split selection to get arguments like "limit=?"
                    String[] args = selection.split(",");
                    for (String arg : args) {
                        // split arguments into key ("limit") and value ("?")
                        String[] keyAndValue = arg.split("=");
                        try {
                            // check if value is a placeholder ("?"), if so replace with the next value of selectionArgs
                            String value = "?".equals(keyAndValue[1].trim()) ? selectionArgs[selectionArgIndex++] : keyAndValue[1];
                            if ("limit".equals(keyAndValue[0].trim())) {
                                limit = Integer.valueOf(value);
                            } else if ("deckID".equals(keyAndValue[0].trim())) {
                                deckIdOfTemporarilySelectedDeck = Long.valueOf(value);
                                if (!selectDeckWithCheck(col, deckIdOfTemporarilySelectedDeck)) {
                                    // if the provided deckID is wrong, return empty cursor.
                                    return rv;
                                }
                            }
                        } catch (NumberFormatException nfe) {
                            nfe.printStackTrace();
                        }
                    }
                }
                // retrieve the number of cards provided by the selection parameter "limit"
                col.getSched().deferReset();
                for (int k = 0; k < limit; k++) {
                    Card currentCard = col.getSched().getCard();
                    if (currentCard == null) {
                        break;
                    }
                    int buttonCount = col.getSched().answerButtons(currentCard);
                    JSONArray buttonTexts = new JSONArray();
                    for (int i = 0; i < buttonCount; i++) {
                        buttonTexts.put(col.getSched().nextIvlStr(mContext, currentCard, i + 1));
                    }
                    addReviewInfoToCursor(currentCard, buttonTexts, buttonCount, rv, col, columns);
                }
                if (deckIdOfTemporarilySelectedDeck != -1) {
                    // if the selected deck was changed
                    // change the selected deck back to the one it was before the query
                    col.getDecks().select(selectedDeckBeforeQuery);
                }
                return rv;
            }
        case DECKS:
            {
                List<DeckDueTreeNode> allDecks = col.getSched().deckDueList();
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, allDecks.size());
                for (DeckDueTreeNode deck : allDecks) {
                    long id = deck.getDid();
                    String name = deck.getFullDeckName();
                    addDeckToCursor(id, name, getDeckCountsFromDueTreeNode(deck), rv, col, columns);
                }
                return rv;
            }
        case DECKS_ID:
            {
                /* Direct access deck */
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                List<DeckDueTreeNode> allDecks = col.getSched().deckDueList();
                long deckId;
                deckId = Long.parseLong(uri.getPathSegments().get(1));
                for (DeckDueTreeNode deck : allDecks) {
                    if (deck.getDid() == deckId) {
                        addDeckToCursor(deckId, deck.getFullDeckName(), getDeckCountsFromDueTreeNode(deck), rv, col, columns);
                        return rv;
                    }
                }
                return rv;
            }
        case DECK_SELECTED:
            {
                long id = col.getDecks().selected();
                String name = col.getDecks().name(id);
                String[] columns = ((projection != null) ? projection : FlashCardsContract.Deck.DEFAULT_PROJECTION);
                MatrixCursor rv = new MatrixCursor(columns, 1);
                JSONArray counts = new JSONArray(Arrays.asList(col.getSched().counts()));
                addDeckToCursor(id, name, counts, rv, col, columns);
                return rv;
            }
        default:
            // Unknown URI type
            throw new IllegalArgumentException("uri " + uri + " is not supported");
    }
}
Also used : JSONArray(com.ichi2.utils.JSONArray) JSONException(com.ichi2.utils.JSONException) MatrixCursor(android.database.MatrixCursor) Card(com.ichi2.libanki.Card) DeckDueTreeNode(com.ichi2.libanki.sched.DeckDueTreeNode) JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) ArrayList(java.util.ArrayList) List(java.util.List) Models(com.ichi2.libanki.Models)

Example 9 with AnswerButtons

use of com.ichi2.anki.reviewer.AnswerButtons in project AnkiChinaAndroid by ankichinateam.

the class SchedTest method test_cram.

@Test
public void test_cram() throws Exception {
    Collection col = getColV1();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    c.setIvl(100);
    c.setQueue(CARD_TYPE_REV);
    c.setType(QUEUE_TYPE_REV);
    // due in 25 days, so it's been waiting 75 days
    c.setDue(col.getSched().getToday() + 25);
    c.setMod(1);
    c.setFactor(STARTING_FACTOR);
    c.startTimer();
    c.flush();
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 0 }, col.getSched().counts());
    Card cardcopy = c.clone();
    // create a dynamic deck and refresh it
    long did = col.getDecks().newDyn("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    // should appear as new in the deck list
    // todo: which sort
    // and should appear in the counts
    assertArrayEquals(new int[] { 1, 0, 0 }, col.getSched().counts());
    // grab it and check estimates
    c = col.getSched().getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, 1));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 2));
    Deck cram = col.getDecks().get(did);
    cram.put("delays", new JSONArray(new double[] { 1, 10 }));
    col.getDecks().save(cram);
    assertEquals(3, col.getSched().answerButtons(c));
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    col.getSched().answerCard(c, 2);
    // elapsed time was 75 days
    // factor = 2.5+1.2/2 = 1.85
    // int(75*1.85) = 138
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    assertEquals(QUEUE_TYPE_LRN, c.getQueue());
    // should be logged as a cram rep
    assertEquals(3, col.getDb().queryLongScalar("select type from revlog order by id desc limit 1"));
    // check ivls again
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    // when it graduates, due is updated
    c = col.getSched().getCard();
    col.getSched().answerCard(c, 2);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getDue());
    assertEquals(QUEUE_TYPE_REV, c.getQueue());
    // and it will have moved back to the previous deck
    assertEquals(1, c.getDid());
    // cram the deck again
    col.getSched().rebuildDyn(did);
    col.reset();
    c = col.getSched().getCard();
    // check ivls again - passing should be idempotent
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(138 * 60 * 60 * 24, col.getSched().nextIvl(c, 3));
    col.getSched().answerCard(c, 2);
    assertEquals(138, c.getIvl());
    assertEquals(138, c.getODue());
    // fail
    col.getSched().answerCard(c, 1);
    assertEquals(60, col.getSched().nextIvl(c, 1));
    assertEquals(600, col.getSched().nextIvl(c, 2));
    assertEquals(SECONDS_PER_DAY, col.getSched().nextIvl(c, 3));
    // delete the deck, returning the card mid-study
    col.getDecks().rem(col.getDecks().selected());
    assertEquals(1, col.getSched().deckDueTree().size());
    c.load();
    assertEquals(1, c.getIvl());
    assertEquals(col.getSched().getToday() + 1, c.getDue());
    // make it due
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 0 }, col.getSched().counts());
    c.setDue(-5);
    c.setIvl(100);
    c.flush();
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 1 }, col.getSched().counts());
    // cram again
    did = col.getDecks().newDyn("Cram");
    col.getSched().rebuildDyn(did);
    col.reset();
    assertArrayEquals(new int[] { 0, 0, 1 }, col.getSched().counts());
    c.load();
    assertEquals(4, col.getSched().answerButtons(c));
    // add a sibling so we can test minSpace, etc
    Card c2 = c.clone();
    c2.setId(0);
    c2.setOrd(1);
    c2.setDue(325);
    c2.flush();
    // should be able to answer it
    c = col.getSched().getCard();
    col.getSched().answerCard(c, 4);
    // it should have been moved back to the original deck
    assertEquals(1, c.getDid());
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 10 with AnswerButtons

use of com.ichi2.anki.reviewer.AnswerButtons in project Anki-Android by ankidroid.

the class SchedV2Test method test_preview.

@Test
public void test_preview() throws Exception {
    // add cards
    Collection col = getColV2();
    Note note = col.newNote();
    note.setItem("Front", "one");
    col.addNote(note);
    Card c = note.cards().get(0);
    Card orig = c.clone();
    Note note2 = col.newNote();
    note2.setItem("Front", "two");
    col.addNote(note2);
    // cram deck
    long did = addDynamicDeck("Cram");
    Deck cram = col.getDecks().get(did);
    cram.put("resched", false);
    col.getDecks().save(cram);
    col.getSched().rebuildDyn(did);
    col.reset();
    // grab the first card
    c = getCard();
    assertEquals(2, col.getSched().answerButtons(c));
    assertEquals(600, col.getSched().nextIvl(c, BUTTON_ONE));
    assertEquals(0, col.getSched().nextIvl(c, BUTTON_TWO));
    // failing it will push its due time back
    long due = c.getDue();
    col.getSched().answerCard(c, BUTTON_ONE);
    assertNotEquals(c.getDue(), due);
    // the other card should come next
    Card c2 = getCard();
    assertNotEquals(c2.getId(), c.getId());
    // passing it will remove it
    col.getSched().answerCard(c2, BUTTON_TWO);
    assertEquals(QUEUE_TYPE_NEW, c2.getQueue());
    assertEquals(0, c2.getReps());
    assertEquals(CARD_TYPE_NEW, c2.getType());
    // the other card should appear again
    c = getCard();
    assertEquals(orig.getId(), c.getId());
    // emptying the filtered deck should restore card
    col.getSched().emptyDyn(did);
    c.load();
    assertEquals(QUEUE_TYPE_NEW, c.getQueue());
    assertEquals(0, c.getReps());
    assertEquals(CARD_TYPE_NEW, c.getType());
}
Also used : Note(com.ichi2.libanki.Note) Collection(com.ichi2.libanki.Collection) Deck(com.ichi2.libanki.Deck) Card(com.ichi2.libanki.Card) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Aggregations

Card (com.ichi2.libanki.Card)10 Collection (com.ichi2.libanki.Collection)10 Note (com.ichi2.libanki.Note)10 RobolectricTest (com.ichi2.anki.RobolectricTest)8 Test (org.junit.Test)8 JSONArray (com.ichi2.utils.JSONArray)6 Deck (com.ichi2.libanki.Deck)4 JSONObject (com.ichi2.utils.JSONObject)4 SuppressLint (android.annotation.SuppressLint)2 MatrixCursor (android.database.MatrixCursor)2 DeckConfig (com.ichi2.libanki.DeckConfig)2 Model (com.ichi2.libanki.Model)2 DeckDueTreeNode (com.ichi2.libanki.sched.DeckDueTreeNode)2 JSONException (com.ichi2.utils.JSONException)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 SharedPreferences (android.content.SharedPreferences)1 View (android.view.View)1 WebView (android.webkit.WebView)1 ImageView (android.widget.ImageView)1