use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class Collection method getChunk.
private int getChunk() {
if (sChunk != 0) {
return sChunk;
}
// the window size is saved in
// io.requery.android.database.CursorWindow.sCursorWindowSize.
// Values are copied here. Ideally, a getter would allow to access it.
final int WINDOW_SIZE_KB = 2048;
int sCursorWindowSize = WINDOW_SIZE_KB * 1024;
// We have the ability to look into our sqlite implementation on Android and use it's value
// as a ceiling. Try it, with a reasonable fallback in case of failure
SupportSQLiteDatabase db = mDb.getDatabase();
if (!(db instanceof DatabaseChangeDecorator)) {
return sChunk;
}
String db_name = ((DatabaseChangeDecorator) db).getWrapped().getClass().getName();
if ("io.requery.android.database.sqlite.SQLiteDatabase".equals(db_name)) {
try {
Field cursorWindowSize = io.requery.android.database.CursorWindow.class.getDeclaredField("sDefaultCursorWindowSize");
cursorWindowSize.setAccessible(true);
int possibleCursorWindowSize = cursorWindowSize.getInt(null);
Timber.d("Reflectively discovered database default cursor window size %d", possibleCursorWindowSize);
if (possibleCursorWindowSize > 0) {
sCursorWindowSize = possibleCursorWindowSize;
} else {
Timber.w("Obtained unusable cursor window size: %d. Using default %d", possibleCursorWindowSize, sCursorWindowSize);
}
} catch (Exception e) {
Timber.w(e, "Unable to get window size from requery cursor.");
}
}
// reduce the actual size a little bit.
sChunk = (int) (sCursorWindowSize * 15. / 16.);
return sChunk;
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class ZipFile method exportInto.
/**
* Export source database into new destination database Note: The following python syntax isn't supported in
* Android: for row in mSrc.db.execute("select * from cards where id in "+ids2str(cids)): therefore we use a
* different method for copying tables
*
* @param path String path to destination database
* @throws JSONException
* @throws IOException
*/
public void exportInto(String path, Context context) throws JSONException, IOException, ImportExportException {
// create a new collection at the target
new File(path).delete();
Collection dst = Storage.Collection(context, path);
mSrc = mCol;
// find cards
Long[] cids = cardIds();
// attach dst to src so we can copy data between them. This isn't done in original libanki as Python more
// flexible
dst.close();
Timber.d("Attach DB");
mSrc.getDb().getDatabase().execSQL("ATTACH '" + path + "' AS DST_DB");
// copy cards, noting used nids (as unique set)
Timber.d("Copy cards");
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.cards select * from cards where id in " + Utils.ids2str(cids));
Set<Long> nids = new HashSet<>(mSrc.getDb().queryLongList("select nid from cards where id in " + Utils.ids2str(cids)));
// notes
Timber.d("Copy notes");
ArrayList<Long> uniqueNids = new ArrayList<>(nids);
String strnids = Utils.ids2str(uniqueNids);
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.notes select * from notes where id in " + strnids);
// remove system tags if not exporting scheduling info
if (!mIncludeSched) {
Timber.d("Stripping system tags from list");
ArrayList<String> srcTags = mSrc.getDb().queryStringList("select tags from notes where id in " + strnids);
ArrayList<Object[]> args = new ArrayList<>(srcTags.size());
Object[] arg = new Object[2];
for (int row = 0; row < srcTags.size(); row++) {
arg[0] = removeSystemTags(srcTags.get(row));
arg[1] = uniqueNids.get(row);
args.add(row, arg);
}
mSrc.getDb().executeMany("UPDATE DST_DB.notes set tags=? where id=?", args);
}
// models used by the notes
Timber.d("Finding models used by notes");
ArrayList<Long> mids = mSrc.getDb().queryLongList("select distinct mid from DST_DB.notes where id in " + strnids);
// card history and revlog
if (mIncludeSched) {
Timber.d("Copy history and revlog");
mSrc.getDb().getDatabase().execSQL("insert into DST_DB.revlog select * from revlog where cid in " + Utils.ids2str(cids));
// reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
} else {
Timber.d("Detaching destination db and reopening");
// first reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
// then need to reset card state
Timber.d("Resetting cards");
dst.getSched().resetCards(cids);
}
// models - start with zero
Timber.d("Copy models");
for (Model m : mSrc.getModels().all()) {
if (mids.contains(m.getLong("id"))) {
Timber.d("Copy models:%s", m.getLong("id"));
dst.getModels().update(m);
}
}
for (Model m : dst.getModels().all()) {
Timber.d("check dst model:%s", m.getLong("id"));
}
// decks
Timber.d("Copy decks");
ArrayList<Long> dids = new ArrayList<>();
if (mDid != null) {
dids.add(mDid);
for (Long x : mSrc.getDecks().children(mDid).values()) {
dids.add(x);
}
}
JSONObject dconfs = new JSONObject();
for (Deck d : mSrc.getDecks().all()) {
if ("1".equals(d.getString("id"))) {
continue;
}
if (mDid != null && !dids.contains(d.getLong("id"))) {
continue;
}
if (d.getInt("dyn") != 1 && d.getLong("conf") != 1L) {
if (mIncludeSched) {
dconfs.put(Long.toString(d.getLong("conf")), true);
}
}
Deck destinationDeck = d.deepClone();
if (!mIncludeSched) {
// scheduling not included, so reset deck settings to default
destinationDeck.put("conf", 1);
}
dst.getDecks().update(destinationDeck);
}
// copy used deck confs
Timber.d("Copy deck options");
for (DeckConfig dc : mSrc.getDecks().allConf()) {
if (dconfs.has(dc.getString("id"))) {
dst.getDecks().updateConf(dc);
}
}
// find used media
Timber.d("Find used media");
JSONObject media = new JSONObject();
mMediaDir = mSrc.getMedia().dir();
if (mIncludeMedia) {
ArrayList<Long> mid = mSrc.getDb().queryLongList("select mid from notes where id in " + strnids);
ArrayList<String> flds = mSrc.getDb().queryStringList("select flds from notes where id in " + strnids);
for (int idx = 0; idx < mid.size(); idx++) {
for (String file : mSrc.getMedia().filesInStr(mid.get(idx), flds.get(idx))) {
// skip files in subdirs
if (file.contains(File.separator)) {
continue;
}
media.put(file, true);
}
}
if (mMediaDir != null) {
for (File f : new File(mMediaDir).listFiles()) {
if (f.isDirectory()) {
continue;
}
String fname = f.getName();
if (fname.startsWith("_")) {
// Loop through every model that will be exported, and check if it contains a reference to f
for (JSONObject model : mSrc.getModels().all()) {
if (_modelHasMedia(model, fname)) {
media.put(fname, true);
break;
}
}
}
}
}
}
JSONArray keys = media.names();
if (keys != null) {
for (int i = 0; i < keys.length(); i++) {
mMediaFiles.add(keys.getString(i));
}
}
Timber.d("Cleanup");
dst.setCrt(mSrc.getCrt());
// todo: tags?
mCount = dst.cardCount();
dst.setMod();
postExport();
dst.close();
}
use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.
the class ContentProviderTest method testInsertTemplate.
/**
* Check that inserting and removing a note into default deck works as expected
*/
@Test
public void testInsertTemplate() throws Exception {
// Get required objects for test
final ContentResolver cr = getContentResolver();
Collection col = getCol();
// Add a new basic model that we use for testing purposes (existing models could potentially be corrupted)
Model model = StdModels.BASIC_MODEL.add(col, BASIC_MODEL_NAME);
long modelId = model.getLong("id");
// Add the note
Uri modelUri = ContentUris.withAppendedId(FlashCardsContract.Model.CONTENT_URI, modelId);
// choose the last one because not the same as the basic model template
int testIndex = TEST_MODEL_CARDS.length - 1;
int expectedOrd = model.getJSONArray("tmpls").length();
ContentValues cv = new ContentValues();
cv.put(FlashCardsContract.CardTemplate.NAME, TEST_MODEL_CARDS[testIndex]);
cv.put(FlashCardsContract.CardTemplate.QUESTION_FORMAT, TEST_MODEL_QFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.ANSWER_FORMAT, TEST_MODEL_AFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.BROWSER_QUESTION_FORMAT, TEST_MODEL_QFMT[testIndex]);
cv.put(FlashCardsContract.CardTemplate.BROWSER_ANSWER_FORMAT, TEST_MODEL_AFMT[testIndex]);
Uri templatesUri = Uri.withAppendedPath(modelUri, "templates");
Uri templateUri = cr.insert(templatesUri, cv);
// test that the changes are physically saved to the DB
col = reopenCol();
assertNotNull("Check template uri", templateUri);
assertEquals("Check template uri ord", expectedOrd, ContentUris.parseId(templateUri));
model = col.getModels().get(modelId);
assertNotNull("Check model", model);
JSONObject template = model.getJSONArray("tmpls").getJSONObject(expectedOrd);
assertEquals("Check template JSONObject ord", expectedOrd, template.getInt("ord"));
assertEquals("Check template name", TEST_MODEL_CARDS[testIndex], template.getString("name"));
assertEquals("Check qfmt", TEST_MODEL_QFMT[testIndex], template.getString("qfmt"));
assertEquals("Check afmt", TEST_MODEL_AFMT[testIndex], template.getString("afmt"));
assertEquals("Check bqfmt", TEST_MODEL_QFMT[testIndex], template.getString("bqfmt"));
assertEquals("Check bafmt", TEST_MODEL_AFMT[testIndex], template.getString("bafmt"));
col.getModels().rem(model);
}
use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.
the class DeckPickerCheckDatabaseListenerTest method validResultWithFailedDatabaseWillShowFailedDialog.
@Test
public void validResultWithFailedDatabaseWillShowFailedDialog() {
CheckDatabaseResult failedDb = failedDatabase();
Pair<Boolean, Collection.CheckDatabaseResult> result = validResultWithData(failedDb);
execute(result);
assertThat("Load Failed dialog should be shown if failed data is supplied", mImpl.didDisplayDialogLoadFailed());
assertThat("Locked Database dialog should be shown if Db was locked", !mImpl.didDisplayLockedDialog());
assertThat("Dialog should not be displayed", !mImpl.didDisplayMessage());
}
use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.
the class Storage method Collection.
public static Collection Collection(Context context, @NonNull String path, boolean server, boolean log, @NonNull Time time) {
assert (path.endsWith(".anki2") || path.endsWith(".anki21"));
File dbFile = new File(path);
boolean create = !dbFile.exists();
DroidBackend backend = DroidBackendFactory.getInstance(useBackend());
DB db = backend.openCollectionDatabase(sUseInMemory ? ":memory:" : path);
try {
// initialize
int ver;
if (create) {
ver = _createDB(db, time, backend);
} else {
ver = _upgradeSchema(db, time);
}
db.execute("PRAGMA temp_store = memory");
// add db to col and do any remaining upgrades
Collection col = backend.createCollection(context, db, path, server, log, time);
if (ver < Consts.SCHEMA_VERSION) {
_upgrade(col, ver);
} else if (ver > Consts.SCHEMA_VERSION) {
throw new RuntimeException("This file requires a newer version of Anki.");
} else if (create) {
addNoteTypes(col, backend);
col.onCreate();
col.save();
}
return col;
} catch (Exception e) {
Timber.e(e, "Error opening collection; closing database");
db.close();
throw e;
}
}
Aggregations