Search in sources :

Example 46 with Template

use of com.ichi2.libanki.template.Template in project AnkiChinaAndroid by ankichinateam.

the class CardContentProvider method insert.

@Override
public Uri insert(Uri uri, ContentValues values) {
    if (!hasReadWritePermission() && shouldEnforceQueryOrInsertSecurity()) {
        throwSecurityException("insert", uri);
    }
    Collection col = CollectionHelper.getInstance().getCol(mContext);
    if (col == null) {
        throw new IllegalStateException(COL_NULL_ERROR_MSG);
    }
    col.log(getLogMessage("insert", uri));
    // Find out what data the user is requesting
    int match = sUriMatcher.match(uri);
    switch(match) {
        case NOTES:
            {
                /* Insert new note with specified fields and tags
                 */
                Long modelId = values.getAsLong(FlashCardsContract.Note.MID);
                String flds = values.getAsString(FlashCardsContract.Note.FLDS);
                String tags = values.getAsString(FlashCardsContract.Note.TAGS);
                // Create empty note
                com.ichi2.libanki.Note newNote = new com.ichi2.libanki.Note(col, col.getModels().get(modelId));
                // Set fields
                String[] fldsArray = Utils.splitFields(flds);
                // Check that correct number of flds specified
                if (fldsArray.length != newNote.getFields().length) {
                    throw new IllegalArgumentException("Incorrect flds argument : " + flds);
                }
                for (int idx = 0; idx < fldsArray.length; idx++) {
                    newNote.setField(idx, fldsArray[idx]);
                }
                // Set tags
                if (tags != null) {
                    newNote.setTagsFromStr(tags);
                }
                // Add to collection
                col.addNote(newNote);
                col.save();
                return Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, Long.toString(newNote.getId()));
            }
        case NOTES_ID:
            // Note ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert note with specific ID");
        case NOTES_ID_CARDS:
            // Cards are generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert cards directly (only through NOTES)");
        case NOTES_ID_CARDS_ORD:
            // Cards are generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert cards directly (only through NOTES)");
        case MODELS:
            // Get input arguments
            String modelName = values.getAsString(FlashCardsContract.Model.NAME);
            String css = values.getAsString(FlashCardsContract.Model.CSS);
            Long did = values.getAsLong(FlashCardsContract.Model.DECK_ID);
            String fieldNames = values.getAsString(FlashCardsContract.Model.FIELD_NAMES);
            Integer numCards = values.getAsInteger(FlashCardsContract.Model.NUM_CARDS);
            Integer sortf = values.getAsInteger(FlashCardsContract.Model.SORT_FIELD_INDEX);
            Integer type = values.getAsInteger(FlashCardsContract.Model.TYPE);
            String latexPost = values.getAsString(FlashCardsContract.Model.LATEX_POST);
            String latexPre = values.getAsString(FlashCardsContract.Model.LATEX_PRE);
            // Throw exception if required fields empty
            if (modelName == null || fieldNames == null || numCards == null) {
                throw new IllegalArgumentException("Model name, field_names, and num_cards can't be empty");
            }
            if (did != null && col.getDecks().isDyn(did)) {
                throw new IllegalArgumentException("Cannot set a filtered deck as default deck for a model");
            }
            // Create a new model
            Models mm = col.getModels();
            Model newModel = mm.newModel(modelName);
            try {
                // Add the fields
                String[] allFields = Utils.splitFields(fieldNames);
                for (String f : allFields) {
                    mm.addFieldInNewModel(newModel, mm.newField(f));
                }
                // Add some empty card templates
                for (int idx = 0; idx < numCards; idx++) {
                    JSONObject t = mm.newTemplate("Card " + (idx + 1));
                    t.put("qfmt", String.format("{{%s}}", allFields[0]));
                    String answerField = allFields[0];
                    if (allFields.length > 1) {
                        answerField = allFields[1];
                    }
                    t.put("afmt", String.format("{{FrontSide}}\\n\\n<hr id=answer>\\n\\n{{%s}}", answerField));
                    mm.addTemplateInNewModel(newModel, t);
                }
                // Add the CSS if specified
                if (css != null) {
                    newModel.put("css", css);
                }
                // Add the did if specified
                if (did != null) {
                    newModel.put("did", did);
                }
                if (sortf != null && sortf < allFields.length) {
                    newModel.put("sortf", sortf);
                }
                if (type != null) {
                    newModel.put("type", type);
                }
                if (latexPost != null) {
                    newModel.put("latexPost", latexPost);
                }
                if (latexPre != null) {
                    newModel.put("latexPre", latexPre);
                }
                // Add the model to collection (from this point on edits will require a full-sync)
                mm.add(newModel);
                col.save();
                // Get the mid and return a URI
                String mid = Long.toString(newModel.getLong("id"));
                return Uri.withAppendedPath(FlashCardsContract.Model.CONTENT_URI, mid);
            } catch (JSONException e) {
                Timber.e(e, "Could not set a field of new model %s", modelName);
                return null;
            }
        case MODELS_ID:
            // Model ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert model with specific ID");
        case MODELS_ID_TEMPLATES:
            {
                Models models = col.getModels();
                Long mid = getModelIdFromUri(uri, col);
                Model existingModel = models.get(mid);
                if (existingModel == null) {
                    throw new IllegalArgumentException("model missing: " + mid);
                }
                String name = values.getAsString(CardTemplate.NAME);
                String qfmt = values.getAsString(CardTemplate.QUESTION_FORMAT);
                String afmt = values.getAsString(CardTemplate.ANSWER_FORMAT);
                String bqfmt = values.getAsString(CardTemplate.BROWSER_QUESTION_FORMAT);
                String bafmt = values.getAsString(CardTemplate.BROWSER_ANSWER_FORMAT);
                try {
                    JSONObject t = models.newTemplate(name);
                    t.put("qfmt", qfmt);
                    t.put("afmt", afmt);
                    t.put("bqfmt", bqfmt);
                    t.put("bafmt", bafmt);
                    models.addTemplate(existingModel, t);
                    models.save(existingModel);
                    col.save();
                    return ContentUris.withAppendedId(uri, t.getInt("ord"));
                } catch (ConfirmModSchemaException e) {
                    throw new IllegalArgumentException("Unable to add template without user requesting/accepting full-sync", e);
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Unable to get ord from new template", e);
                }
            }
        case MODELS_ID_TEMPLATES_ID:
            throw new IllegalArgumentException("Not possible to insert template with specific ORD");
        case MODELS_ID_FIELDS:
            {
                Models models = col.getModels();
                Long mid = getModelIdFromUri(uri, col);
                Model existingModel = models.get(mid);
                if (existingModel == null) {
                    throw new IllegalArgumentException("model missing: " + mid);
                }
                String name = values.getAsString(FlashCardsContract.Model.FIELD_NAME);
                if (name == null) {
                    throw new IllegalArgumentException("field name missing for model: " + mid);
                }
                JSONObject field = models.newField(name);
                try {
                    models.addField(existingModel, field);
                    col.save();
                    JSONArray flds = existingModel.getJSONArray("flds");
                    return ContentUris.withAppendedId(uri, flds.length() - 1);
                } catch (ConfirmModSchemaException e) {
                    throw new IllegalArgumentException("Unable to insert field: " + name, e);
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Unable to get newly created field: " + name, e);
                }
            }
        case SCHEDULE:
            // Doesn't make sense to insert an object into the schedule table
            throw new IllegalArgumentException("Not possible to perform insert operation on schedule");
        case DECKS:
            // Insert new deck with specified name
            String deckName = values.getAsString(FlashCardsContract.Deck.DECK_NAME);
            did = col.getDecks().id(deckName, false);
            if (did != null) {
                throw new IllegalArgumentException("Deck name already exists: " + deckName);
            }
            if (!Decks.isValidDeckName(deckName)) {
                throw new IllegalArgumentException("Invalid deck name '" + deckName + "'");
            }
            did = col.getDecks().id(deckName, true);
            Deck deck = col.getDecks().get(did);
            if (deck != null) {
                try {
                    String deckDesc = values.getAsString(FlashCardsContract.Deck.DECK_DESC);
                    if (deckDesc != null) {
                        deck.put("desc", deckDesc);
                    }
                } catch (JSONException e) {
                    Timber.e(e, "Could not set a field of new deck %s", deckName);
                    return null;
                }
            }
            col.getDecks().flush();
            return Uri.withAppendedPath(FlashCardsContract.Deck.CONTENT_ALL_URI, Long.toString(did));
        case DECK_SELECTED:
            // Can't have more than one selected deck
            throw new IllegalArgumentException("Selected deck can only be queried and updated");
        case DECKS_ID:
            // Deck ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert deck with specific ID");
        default:
            // Unknown URI type
            throw new IllegalArgumentException("uri " + uri + " is not supported");
    }
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) JSONException(com.ichi2.utils.JSONException) Deck(com.ichi2.libanki.Deck) JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) Models(com.ichi2.libanki.Models)

Example 47 with Template

use of com.ichi2.libanki.template.Template in project AnkiChinaAndroid by ankichinateam.

the class CardContentProvider method update.

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    if (!hasReadWritePermission() && shouldEnforceUpdateSecurity(uri)) {
        throwSecurityException("update", uri);
    }
    Collection col = CollectionHelper.getInstance().getCol(mContext);
    if (col == null) {
        throw new IllegalStateException(COL_NULL_ERROR_MSG);
    }
    col.log(getLogMessage("update", uri));
    // Find out what data the user is requesting
    int match = sUriMatcher.match(uri);
    // Number of updated entries (return value)
    int updated = 0;
    switch(match) {
        case NOTES_V2:
        case NOTES:
            throw new IllegalArgumentException("Not possible to update notes directly (only through data URI)");
        case NOTES_ID:
            {
                /* Direct access note details
                 */
                Note currentNote = getNoteFromUri(uri, col);
                // the key of the ContentValues contains the column name
                // the value of the ContentValues contains the row value.
                Set<Map.Entry<String, Object>> valueSet = values.valueSet();
                for (Map.Entry<String, Object> entry : valueSet) {
                    String key = entry.getKey();
                    // when the client does not specify FLDS, then don't update the FLDS
                    if (key.equals(FlashCardsContract.Note.FLDS)) {
                        // Update FLDS
                        Timber.d("CardContentProvider: flds update...");
                        String newFldsEncoded = (String) entry.getValue();
                        String[] flds = Utils.splitFields(newFldsEncoded);
                        // Check that correct number of flds specified
                        if (flds.length != currentNote.getFields().length) {
                            throw new IllegalArgumentException("Incorrect flds argument : " + newFldsEncoded);
                        }
                        // Update the note
                        for (int idx = 0; idx < flds.length; idx++) {
                            currentNote.setField(idx, flds[idx]);
                        }
                        updated++;
                    } else if (key.equals(FlashCardsContract.Note.TAGS)) {
                        // Update tags
                        Timber.d("CardContentProvider: tags update...");
                        Object tags = entry.getValue();
                        if (tags != null) {
                            currentNote.setTagsFromStr(String.valueOf(tags));
                        }
                        updated++;
                    } else {
                        // Unsupported column
                        throw new IllegalArgumentException("Unsupported column: " + key);
                    }
                }
                Timber.d("CardContentProvider: Saving note...");
                currentNote.flush();
                break;
            }
        case NOTES_ID_CARDS:
            // TODO: To be implemented
            throw new UnsupportedOperationException("Not yet implemented");
        // break;
        case NOTES_ID_CARDS_ORD:
            {
                Card currentCard = getCardFromUri(uri, col);
                boolean isDeckUpdate = false;
                long did = -1;
                // the key of the ContentValues contains the column name
                // the value of the ContentValues contains the row value.
                Set<Map.Entry<String, Object>> valueSet = values.valueSet();
                for (Map.Entry<String, Object> entry : valueSet) {
                    // Only updates on deck id is supported
                    String key = entry.getKey();
                    isDeckUpdate = key.equals(FlashCardsContract.Card.DECK_ID);
                    did = values.getAsLong(key);
                }
                if (col.getDecks().isDyn(did)) {
                    throw new IllegalArgumentException("Cards cannot be moved to a filtered deck");
                }
                /* now update the card
                 */
                if ((isDeckUpdate) && (did >= 0)) {
                    Timber.d("CardContentProvider: Moving card to other deck...");
                    col.getDecks().flush();
                    currentCard.setDid(did);
                    currentCard.flush();
                    col.save();
                    updated++;
                } else {
                    // User tries an operation that is not (yet?) supported.
                    throw new IllegalArgumentException("Currently only updates of decks are supported");
                }
                break;
            }
        case MODELS:
            throw new IllegalArgumentException("Cannot update models in bulk");
        case MODELS_ID:
            // Get the input parameters
            String newModelName = values.getAsString(FlashCardsContract.Model.NAME);
            String newCss = values.getAsString(FlashCardsContract.Model.CSS);
            String newDid = values.getAsString(FlashCardsContract.Model.DECK_ID);
            String newFieldList = values.getAsString(FlashCardsContract.Model.FIELD_NAMES);
            if (newFieldList != null) {
                // Changing the field names would require a full-sync
                throw new IllegalArgumentException("Field names cannot be changed via provider");
            }
            Integer newSortf = values.getAsInteger(FlashCardsContract.Model.SORT_FIELD_INDEX);
            Integer newType = values.getAsInteger(FlashCardsContract.Model.TYPE);
            String newLatexPost = values.getAsString(FlashCardsContract.Model.LATEX_POST);
            String newLatexPre = values.getAsString(FlashCardsContract.Model.LATEX_PRE);
            // Get the original note JSON
            Model model = col.getModels().get(getModelIdFromUri(uri, col));
            try {
                // Update model name and/or css
                if (newModelName != null) {
                    model.put("name", newModelName);
                    updated++;
                }
                if (newCss != null) {
                    model.put("css", newCss);
                    updated++;
                }
                if (newDid != null) {
                    if (col.getDecks().isDyn(Long.parseLong(newDid))) {
                        throw new IllegalArgumentException("Cannot set a filtered deck as default deck for a model");
                    }
                    model.put("did", newDid);
                    updated++;
                }
                if (newSortf != null) {
                    model.put("sortf", newSortf);
                    updated++;
                }
                if (newType != null) {
                    model.put("type", newType);
                    updated++;
                }
                if (newLatexPost != null) {
                    model.put("latexPost", newLatexPost);
                    updated++;
                }
                if (newLatexPre != null) {
                    model.put("latexPre", newLatexPre);
                    updated++;
                }
                col.getModels().save(model);
                col.save();
            } catch (JSONException e) {
                Timber.e(e, "JSONException updating model");
            }
            break;
        case MODELS_ID_TEMPLATES:
            throw new IllegalArgumentException("Cannot update templates in bulk");
        case MODELS_ID_TEMPLATES_ID:
            Long mid = values.getAsLong(CardTemplate.MODEL_ID);
            Integer ord = values.getAsInteger(CardTemplate.ORD);
            String name = values.getAsString(CardTemplate.NAME);
            String qfmt = values.getAsString(CardTemplate.QUESTION_FORMAT);
            String afmt = values.getAsString(CardTemplate.ANSWER_FORMAT);
            String bqfmt = values.getAsString(CardTemplate.BROWSER_QUESTION_FORMAT);
            String bafmt = values.getAsString(CardTemplate.BROWSER_ANSWER_FORMAT);
            // Throw exception if read-only fields are included
            if (mid != null || ord != null) {
                throw new IllegalArgumentException("Updates to mid or ord are not allowed");
            }
            // Update the model
            try {
                Integer templateOrd = Integer.parseInt(uri.getLastPathSegment());
                Model existingModel = col.getModels().get(getModelIdFromUri(uri, col));
                JSONArray templates = existingModel.getJSONArray("tmpls");
                JSONObject template = templates.getJSONObject(templateOrd);
                if (name != null) {
                    template.put("name", name);
                    updated++;
                }
                if (qfmt != null) {
                    template.put("qfmt", qfmt);
                    updated++;
                }
                if (afmt != null) {
                    template.put("afmt", afmt);
                    updated++;
                }
                if (bqfmt != null) {
                    template.put("bqfmt", bqfmt);
                    updated++;
                }
                if (bafmt != null) {
                    template.put("bafmt", bafmt);
                    updated++;
                }
                // Save the model
                templates.put(templateOrd, template);
                existingModel.put("tmpls", templates);
                col.getModels().save(existingModel, true);
                col.save();
            } catch (JSONException e) {
                throw new IllegalArgumentException("Model is malformed", e);
            }
            break;
        case SCHEDULE:
            {
                Set<Map.Entry<String, Object>> valueSet = values.valueSet();
                int cardOrd = -1;
                long noteID = -1;
                int ease = -1;
                long timeTaken = -1;
                int bury = -1;
                int suspend = -1;
                for (Map.Entry<String, Object> entry : valueSet) {
                    String key = entry.getKey();
                    if (key.equals(FlashCardsContract.ReviewInfo.NOTE_ID)) {
                        noteID = values.getAsLong(key);
                    } else if (key.equals(FlashCardsContract.ReviewInfo.CARD_ORD)) {
                        cardOrd = values.getAsInteger(key);
                    } else if (key.equals(FlashCardsContract.ReviewInfo.EASE)) {
                        ease = values.getAsInteger(key);
                    } else if (key.equals(FlashCardsContract.ReviewInfo.TIME_TAKEN)) {
                        timeTaken = values.getAsLong(key);
                    } else if (key.equals(FlashCardsContract.ReviewInfo.BURY)) {
                        bury = values.getAsInteger(key);
                    } else if (key.equals(FlashCardsContract.ReviewInfo.SUSPEND)) {
                        suspend = values.getAsInteger(key);
                    }
                }
                if (cardOrd != -1 && noteID != -1) {
                    Card cardToAnswer = getCard(noteID, cardOrd, col);
                    if (cardToAnswer != null) {
                        if (bury == 1) {
                            // bury card
                            buryOrSuspendCard(col, col.getSched(), cardToAnswer, true);
                        } else if (suspend == 1) {
                            // suspend card
                            buryOrSuspendCard(col, col.getSched(), cardToAnswer, false);
                        } else {
                            answerCard(col, col.getSched(), cardToAnswer, ease, timeTaken);
                        }
                        updated++;
                    } else {
                        Timber.e("Requested card with noteId %d and cardOrd %d was not found. Either the provided " + "noteId/cardOrd were wrong or the card has been deleted in the meantime.", noteID, cardOrd);
                    }
                }
                break;
            }
        case DECKS:
            throw new IllegalArgumentException("Can't update decks in bulk");
        case DECKS_ID:
            // TODO: be sure to throw exception if change to the dyn value of a deck is requested
            throw new UnsupportedOperationException("Not yet implemented");
        case DECK_SELECTED:
            {
                Set<Map.Entry<String, Object>> valueSet = values.valueSet();
                for (Map.Entry<String, Object> entry : valueSet) {
                    String key = entry.getKey();
                    if (key.equals(FlashCardsContract.Deck.DECK_ID)) {
                        long deckId = values.getAsLong(key);
                        if (selectDeckWithCheck(col, deckId)) {
                            updated++;
                        }
                    }
                }
                col.save();
                break;
            }
        default:
            // Unknown URI type
            throw new IllegalArgumentException("uri " + uri + " is not supported");
    }
    return updated;
}
Also used : Set(java.util.Set) JSONArray(com.ichi2.utils.JSONArray) JSONException(com.ichi2.utils.JSONException) Card(com.ichi2.libanki.Card) JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) JSONObject(com.ichi2.utils.JSONObject) Map(java.util.Map)

Example 48 with Template

use of com.ichi2.libanki.template.Template 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 49 with Template

use of com.ichi2.libanki.template.Template in project AnkiChinaAndroid by ankichinateam.

the class CardTemplateEditorTest method testDeleteTemplateWithGeneratedCards.

/**
 * Normal template deletion - with no selective generation should of course work
 */
@SuppressWarnings("PMD.ExcessiveMethodLength")
@Test
public void testDeleteTemplateWithGeneratedCards() {
    String modelName = "Basic (and reversed card)";
    Model collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
    // Start the CardTemplateEditor with a specific model, and make sure the model starts unchanged
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
    ActivityController<CardTemplateEditor> templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
    saveControllerForCleanup(templateEditorController);
    CardTemplateEditor testEditor = (CardTemplateEditor) templateEditorController.get();
    Assert.assertFalse("Model should not have changed yet", testEditor.modelHasChanged());
    Assert.assertEquals("Model should have 2 templates now", 2, testEditor.getTempModel().getTemplateCount());
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
    // Create note with forward and back info
    Note selectiveGeneratedNote = getCol().newNote(collectionBasicModelOriginal);
    selectiveGeneratedNote.setField(0, "TestFront");
    selectiveGeneratedNote.setField(1, "TestBack");
    getCol().addNote(selectiveGeneratedNote);
    Assert.assertEquals("card generation should result in two cards", 2, getModelCardCount(collectionBasicModelOriginal));
    // Test if we can delete the template - should be possible - but cancel the delete
    ShadowActivity shadowTestEditor = shadowOf(testEditor);
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Did not show dialog about deleting template and it's card?", getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), getDialogText(true));
    clickDialogButton(DialogAction.NEGATIVE, true);
    advanceRobolectricLooper();
    Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0 }));
    Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1 }));
    Assert.assertNull("Can delete both templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 1 }));
    Assert.assertEquals("Change in database despite no change?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    Assert.assertEquals("Model should have 2 templates still", 2, testEditor.getTempModel().getTemplateCount());
    // Add a template - click add, click confirm for card add, click confirm again for full sync
    shadowTestEditor.clickMenuItem(R.id.action_add);
    advanceRobolectricLooper();
    Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
    Assert.assertEquals("Change added but not adjusted correctly?", 2, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 0));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
    Assert.assertTrue("Ordinal not pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 2));
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
    advanceRobolectricLooper();
    Assert.assertFalse("Model should now be unchanged", testEditor.modelHasChanged());
    Assert.assertEquals("card generation should result in three cards", 3, getModelCardCount(collectionBasicModelOriginal));
    // reload the model for future comparison after saving the edit
    collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
    // Start the CardTemplateEditor back up after saving (which closes the thing...)
    intent = new Intent(Intent.ACTION_VIEW);
    intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
    templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
    testEditor = (CardTemplateEditor) templateEditorController.get();
    shadowTestEditor = shadowOf(testEditor);
    Assert.assertFalse("Model should not have changed yet", testEditor.modelHasChanged());
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 2));
    Assert.assertEquals("Model should have 3 templates now", 3, testEditor.getTempModel().getTemplateCount());
    // Add another template - but we work in memory for a while before saving
    shadowTestEditor.clickMenuItem(R.id.action_add);
    advanceRobolectricLooper();
    Assert.assertEquals("Change added but not adjusted correctly?", 3, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 0));
    Assert.assertTrue("Model should have changed", testEditor.modelHasChanged());
    Assert.assertEquals("Model should have 4 templates now", 4, testEditor.getTempModel().getTemplateCount());
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 0));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 1));
    Assert.assertFalse("Ordinal pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 2));
    Assert.assertTrue("Ordinal not pending add?", TemporaryModel.isOrdinalPendingAdd(testEditor.getTempModel(), 3));
    Assert.assertEquals("Change added but not adjusted correctly?", 3, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 0));
    // Delete two pre-existing templates for real now - but still without saving it out, should work fine
    advanceRobolectricLooper();
    testEditor.mViewPager.setCurrentItem(0);
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Did not show dialog about deleting template and it's card?", getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 1"), getDialogText(true));
    clickDialogButton(DialogAction.POSITIVE, true);
    advanceRobolectricLooper();
    advanceRobolectricLooper();
    testEditor.mViewPager.setCurrentItem(0);
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_delete));
    advanceRobolectricLooper();
    Assert.assertEquals("Did not show dialog about deleting template and it's card?", getQuantityString(R.plurals.card_template_editor_confirm_delete, 1, 1, "Card 2"), getDialogText(true));
    clickDialogButton(DialogAction.POSITIVE, true);
    advanceRobolectricLooper();
    // - assert can delete any 1 or 2 Card templates but not all
    Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0 }));
    Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1 }));
    Assert.assertNotNull("Cannot delete template?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 2 }));
    Assert.assertNotNull("Cannot delete two templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 1 }));
    Assert.assertNotNull("Cannot delete two templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 2 }));
    Assert.assertNotNull("Cannot delete two templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 1, 2 }));
    Assert.assertNull("Can delete all templates?", getCol().getModels().getCardIdsForModel(collectionBasicModelOriginal.getLong("id"), new int[] { 0, 1, 2 }));
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    Assert.assertEquals("Change added but not adjusted correctly?", 1, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 0));
    Assert.assertEquals("Change incorrectly pending add?", -1, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 1));
    Assert.assertEquals("Change incorrectly pending add?", -1, TemporaryModel.getAdjustedAddOrdinalAtChangeIndex(testEditor.getTempModel(), 2));
    // Now confirm everything to persist it to the database
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
    advanceRobolectricLooper();
    advanceRobolectricLooper();
    Assert.assertNotEquals("Change not in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    Assert.assertEquals("Model should have 2 templates now", 2, getCurrentDatabaseModelCopy(modelName).getJSONArray("tmpls").length());
    Assert.assertEquals("should be two cards", 2, getModelCardCount(collectionBasicModelOriginal));
}
Also used : Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) ShadowActivity(org.robolectric.shadows.ShadowActivity) ShadowIntent(org.robolectric.shadows.ShadowIntent) Intent(android.content.Intent) Test(org.junit.Test)

Example 50 with Template

use of com.ichi2.libanki.template.Template in project AnkiChinaAndroid by ankichinateam.

the class CardTemplateEditorTest method testEditTemplateContents.

@Test
@SuppressWarnings("PMD.NPathComplexity")
public void testEditTemplateContents() throws Exception {
    String modelName = "Basic";
    // Start the CardTemplateEditor with a specific model, and make sure the model starts unchanged
    JSONObject collectionBasicModelOriginal = getCurrentDatabaseModelCopy(modelName);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.putExtra("modelId", collectionBasicModelOriginal.getLong("id"));
    ActivityController<CardTemplateEditor> templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
    saveControllerForCleanup(templateEditorController);
    CardTemplateEditor testEditor = (CardTemplateEditor) templateEditorController.get();
    Assert.assertFalse("Model should not have changed yet", testEditor.modelHasChanged());
    // Change the model and make sure it registers as changed, but the database is unchanged
    EditText templateFront = testEditor.findViewById(R.id.front_edit);
    String TEST_MODEL_QFMT_EDIT = "!@#$%^&*TEST*&^%$#@!";
    templateFront.getText().append(TEST_MODEL_QFMT_EDIT);
    advanceRobolectricLooper();
    Assert.assertTrue("Model did not change after edit?", testEditor.modelHasChanged());
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    // Kill and restart the Activity, make sure model edit is preserved
    Bundle outBundle = new Bundle();
    templateEditorController.saveInstanceState(outBundle);
    templateEditorController.pause().stop().destroy();
    templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class).create(outBundle).start().resume().visible();
    saveControllerForCleanup(templateEditorController);
    testEditor = (CardTemplateEditor) templateEditorController.get();
    ShadowActivity shadowTestEditor = shadowOf(testEditor);
    Assert.assertTrue("model change not preserved across activity lifecycle?", testEditor.modelHasChanged());
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    // Make sure we get a confirmation dialog if we hit the back button
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(android.R.id.home));
    advanceRobolectricLooper();
    Assert.assertEquals("Wrong dialog shown?", getDialogText(true), getResourceString(R.string.discard_unsaved_changes));
    clickDialogButton(DialogAction.NEGATIVE, true);
    advanceRobolectricLooper();
    Assert.assertTrue("model change not preserved despite canceling back button?", testEditor.modelHasChanged());
    // Make sure we things are cleared out after a cancel
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(android.R.id.home));
    Assert.assertEquals("Wrong dialog shown?", getDialogText(true), getResourceString(R.string.discard_unsaved_changes));
    clickDialogButton(DialogAction.POSITIVE, true);
    advanceRobolectricLooper();
    Assert.assertFalse("model change not cleared despite discarding changes?", testEditor.modelHasChanged());
    // Get going for content edit assertions again...
    templateEditorController = Robolectric.buildActivity(CardTemplateEditor.class, intent).create().start().resume().visible();
    saveControllerForCleanup(templateEditorController);
    testEditor = (CardTemplateEditor) templateEditorController.get();
    shadowTestEditor = shadowOf(testEditor);
    templateFront = testEditor.findViewById(R.id.front_edit);
    templateFront.getText().append(TEST_MODEL_QFMT_EDIT);
    advanceRobolectricLooper();
    Assert.assertTrue("Model did not change after edit?", testEditor.modelHasChanged());
    // Make sure we pass the edit to the Previewer
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_preview));
    advanceRobolectricLooper();
    Intent startedIntent = shadowTestEditor.getNextStartedActivity();
    ShadowIntent shadowIntent = shadowOf(startedIntent);
    advanceRobolectricLooper();
    Assert.assertEquals("Previewer not started?", CardTemplatePreviewer.class.getName(), shadowIntent.getIntentClass().getName());
    Assert.assertNotNull("intent did not have model JSON filename?", startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME));
    Assert.assertNotEquals("Model sent to Previewer is unchanged?", testEditor.getTempModel().getModel(), TemporaryModel.getTempModel(startedIntent.getStringExtra(TemporaryModel.INTENT_MODEL_FILENAME)));
    Assert.assertEquals("Change already in database?", collectionBasicModelOriginal.toString().trim(), getCurrentDatabaseModelCopy(modelName).toString().trim());
    shadowTestEditor.receiveResult(startedIntent, Activity.RESULT_OK, new Intent());
    // Save the template then fetch it from the collection to see if it was saved correctly
    JSONObject testEditorModelEdited = testEditor.getTempModel().getModel();
    advanceRobolectricLooper();
    Assert.assertTrue("Unable to click?", shadowTestEditor.clickMenuItem(R.id.action_confirm));
    advanceRobolectricLooper();
    JSONObject collectionBasicModelCopyEdited = getCurrentDatabaseModelCopy(modelName);
    Assert.assertNotEquals("model is unchanged?", collectionBasicModelOriginal, collectionBasicModelCopyEdited);
    Assert.assertEquals("model did not save?", testEditorModelEdited.toString().trim(), collectionBasicModelCopyEdited.toString().trim());
    Assert.assertTrue("model does not have our change?", collectionBasicModelCopyEdited.toString().contains(TEST_MODEL_QFMT_EDIT));
}
Also used : EditText(android.widget.EditText) ShadowIntent(org.robolectric.shadows.ShadowIntent) JSONObject(com.ichi2.utils.JSONObject) Bundle(android.os.Bundle) ShadowActivity(org.robolectric.shadows.ShadowActivity) ShadowIntent(org.robolectric.shadows.ShadowIntent) Intent(android.content.Intent) Test(org.junit.Test)

Aggregations

JSONObject (com.ichi2.utils.JSONObject)53 Test (org.junit.Test)35 JSONArray (com.ichi2.utils.JSONArray)28 Model (com.ichi2.libanki.Model)24 ArrayList (java.util.ArrayList)16 Intent (android.content.Intent)14 RobolectricTest (com.ichi2.anki.RobolectricTest)14 Collection (com.ichi2.libanki.Collection)12 Note (com.ichi2.libanki.Note)12 SuppressLint (android.annotation.SuppressLint)9 HashMap (java.util.HashMap)8 Bundle (android.os.Bundle)7 JSONException (com.ichi2.utils.JSONException)7 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)6 Card (com.ichi2.libanki.Card)6 ShadowActivity (org.robolectric.shadows.ShadowActivity)6 ShadowIntent (org.robolectric.shadows.ShadowIntent)6 View (android.view.View)5 ContentResolver (android.content.ContentResolver)4 ContentValues (android.content.ContentValues)4