Search in sources :

Example 6 with CREATED

use of com.ichi2.anki.CardBrowser.Column.CREATED in project Anki-Android by ankidroid.

the class CollectionHelper method initializeAnkiDroidDirectory.

/**
 * Create the AnkiDroid directory if it doesn't exist and add a .nomedia file to it if needed.
 *
 * The AnkiDroid directory is a user preference stored under {@link #PREF_DECK_PATH}, and a sensible
 * default is chosen if the preference hasn't been created yet (i.e., on the first run).
 *
 * The presence of a .nomedia file indicates to media scanners that the directory must be
 * excluded from their search. We need to include this to avoid media scanners including
 * media files from the collection.media directory. The .nomedia file works at the directory
 * level, so placing it in the AnkiDroid directory will ensure media scanners will also exclude
 * the collection.media sub-directory.
 *
 * @param path  Directory to initialize
 * @throws StorageAccessException If no write access to directory
 */
public static synchronized void initializeAnkiDroidDirectory(String path) throws StorageAccessException {
    // Create specified directory if it doesn't exit
    File dir = new File(path);
    if (!dir.exists() && !dir.mkdirs()) {
        throw new StorageAccessException("Failed to create AnkiDroid directory " + path);
    }
    if (!dir.canWrite()) {
        throw new StorageAccessException("No write access to AnkiDroid directory " + path);
    }
    // Add a .nomedia file to it if it doesn't exist
    File nomedia = new File(dir, ".nomedia");
    if (!nomedia.exists()) {
        try {
            nomedia.createNewFile();
        } catch (IOException e) {
            throw new StorageAccessException("Failed to create .nomedia file", e);
        }
    }
}
Also used : StorageAccessException(com.ichi2.anki.exception.StorageAccessException) IOException(java.io.IOException) File(java.io.File)

Example 7 with CREATED

use of com.ichi2.anki.CardBrowser.Column.CREATED in project Anki-Android by ankidroid.

the class CardContentProvider method insert.

@Override
public Uri insert(@NonNull 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);
                Models.AllowEmpty allowEmpty = Models.AllowEmpty.fromBoolean(values.getAsBoolean(FlashCardsContract.Note.ALLOW_EMPTY));
                // 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, allowEmpty);
                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:
        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
            ModelManager 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++) {
                    String card_name = mContext.getResources().getString(R.string.card_n_name, idx + 1);
                    JSONObject t = Models.newTemplate(card_name);
                    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:
            {
                ModelManager 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:
            {
                ModelManager 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_for_name(deckName);
            if (did != null) {
                throw new IllegalArgumentException("Deck name already exists: " + deckName);
            }
            if (!Decks.isValidDeckName(deckName)) {
                throw new IllegalArgumentException("Invalid deck name '" + deckName + "'");
            }
            try {
                did = col.getDecks().id(deckName);
            } catch (DeckRenameException filteredSubdeck) {
                throw new IllegalArgumentException(filteredSubdeck.getMessage());
            }
            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");
        case MEDIA:
            // contentvalue should have data and preferredFileName values
            return insertMediaFile(values, col);
        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) ModelManager(com.ichi2.libanki.ModelManager) DeckRenameException(com.ichi2.libanki.backend.exception.DeckRenameException) 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)

Example 8 with CREATED

use of com.ichi2.anki.CardBrowser.Column.CREATED in project Anki-Android by ankidroid.

the class Sched method _updateCutoff.

/**
 * Daily cutoff ************************************************************* **********************************
 * This function uses GregorianCalendar so as to be sensitive to leap years, daylight savings, etc.
 */
@Override
public void _updateCutoff() {
    Integer oldToday = mToday;
    // days since col created
    mToday = (int) ((getTime().intTime() - mCol.getCrt()) / SECONDS_PER_DAY);
    // end of day cutoff
    mDayCutoff = mCol.getCrt() + ((mToday + 1) * SECONDS_PER_DAY);
    if (!mToday.equals(oldToday)) {
        mCol.log(mToday, mDayCutoff);
    }
    // instead
    for (Deck deck : mCol.getDecks().all()) {
        update(deck);
    }
    // unbury if the day has rolled over
    int unburied = mCol.get_config("lastUnburied", 0);
    if (unburied < mToday) {
        SyncStatus.ignoreDatabaseModification(this::unburyCards);
    }
}
Also used : Deck(com.ichi2.libanki.Deck)

Example 9 with CREATED

use of com.ichi2.anki.CardBrowser.Column.CREATED in project Anki-Android by ankidroid.

the class NoteServiceTest method importAudioWithSameNameTest.

/**
 * Tests if after importing:
 *
 * * New file keeps its name
 * * File with same name, but different content, has its name changed
 * * File with same name and content don't have its name changed
 *
 * @throws IOException if new created files already exist on temp directory
 */
@Test
public void importAudioWithSameNameTest() throws IOException {
    File f1 = directory.newFile("audio.mp3");
    File f2 = directory2.newFile("audio.mp3");
    // write a line in the file so the file's length isn't 0
    try (FileWriter fileWriter = new FileWriter(f1)) {
        fileWriter.write("1");
    }
    // do the same to the second file, but with different data
    try (FileWriter fileWriter = new FileWriter(f2)) {
        fileWriter.write("2");
    }
    MediaClipField fld1 = new MediaClipField();
    fld1.setAudioPath(f1.getAbsolutePath());
    MediaClipField fld2 = new MediaClipField();
    fld2.setAudioPath(f2.getAbsolutePath());
    // third field to test if name is kept after reimporting the same file
    MediaClipField fld3 = new MediaClipField();
    fld3.setAudioPath(f1.getAbsolutePath());
    NoteService.importMediaToDirectory(mTestCol, fld1);
    File o1 = new File(mTestCol.getMedia().dir(), f1.getName());
    NoteService.importMediaToDirectory(mTestCol, fld2);
    File o2 = new File(mTestCol.getMedia().dir(), f2.getName());
    NoteService.importMediaToDirectory(mTestCol, fld3);
    // creating a third outfile isn't necessary because it should be equal to the first one
    assertEquals("path should be equal to the new file made in NoteService.importMediaToDirectory", o1.getAbsolutePath(), fld1.getAudioPath());
    assertNotEquals("path should be different to the new file made in NoteService.importMediaToDirectory", o2.getAbsolutePath(), fld2.getAudioPath());
    assertEquals("path should be equal to the new file made in NoteService.importMediaToDirectory", o1.getAbsolutePath(), fld3.getAudioPath());
}
Also used : FileWriter(java.io.FileWriter) MediaClipField(com.ichi2.anki.multimediacard.fields.MediaClipField) File(java.io.File) FileSystemUtilsKt.createTransientFile(com.ichi2.testutils.FileSystemUtilsKt.createTransientFile) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 10 with CREATED

use of com.ichi2.anki.CardBrowser.Column.CREATED in project Anki-Android by ankidroid.

the class ContentProviderTest method tearDown.

/**
 * Remove the notes and decks created in setUp().
 */
@After
public void tearDown() throws Exception {
    Timber.i("tearDown()");
    if (!mTearDown) {
        return;
    }
    final Collection col = getCol();
    // Delete all notes
    List<Long> remnantNotes = col.findNotes("tag:" + TEST_TAG);
    if (!remnantNotes.isEmpty()) {
        long[] noteIds = Utils.collection2Array(remnantNotes);
        col.remNotes(noteIds);
        col.save();
        assertEquals("Check that remnant notes have been deleted", 0, col.findNotes("tag:" + TEST_TAG).size());
    }
    // delete test decks
    for (long did : mTestDeckIds) {
        col.getDecks().rem(did, true, true);
    }
    col.getDecks().flush();
    assertEquals("Check that all created decks have been deleted", mNumDecksBeforeTest, col.getDecks().count());
    // Delete test model
    col.modSchemaNoCheck();
    removeAllModelsByName(col, BASIC_MODEL_NAME);
    removeAllModelsByName(col, TEST_MODEL_NAME);
}
Also used : Collection(com.ichi2.libanki.Collection) After(org.junit.After)

Aggregations

Collection (com.ichi2.libanki.Collection)9 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)7 JSONException (com.ichi2.utils.JSONException)7 File (java.io.File)7 Intent (android.content.Intent)5 Model (com.ichi2.libanki.Model)5 SharedPreferences (android.content.SharedPreferences)4 Uri (android.net.Uri)4 Bundle (android.os.Bundle)4 View (android.view.View)4 TextView (android.widget.TextView)4 Deck (com.ichi2.libanki.Deck)4 Manifest (android.Manifest)3 BroadcastReceiver (android.content.BroadcastReceiver)3 Context (android.content.Context)3 IntentFilter (android.content.IntentFilter)3 PackageManager (android.content.pm.PackageManager)3 Resources (android.content.res.Resources)3 Message (android.os.Message)3 DeckRenameException (com.ichi2.libanki.backend.exception.DeckRenameException)3