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");
}
}
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;
}
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");
}
}
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));
}
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));
}
Aggregations