use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class Media method mediaChangesZip.
/**
* Media syncing: zips
* ***********************************************************
*/
/**
* Unlike python, our temp zip file will be on disk instead of in memory. This avoids storing
* potentially large files in memory which is not feasible with Android's limited heap space.
* <p>
* Notes:
* <p>
* - The maximum size of the changes zip is decided by the constant SYNC_ZIP_SIZE. If a media file exceeds this
* limit, only that file (in full) will be zipped to be sent to the server.
* <p>
* - This method will be repeatedly called from MediaSyncer until there are no more files (marked "dirty" in the DB)
* to send.
* <p>
* - Since AnkiDroid avoids scanning the media folder on every sync, it is possible for a file to be marked as a
* new addition but actually have been deleted (e.g., with a file manager). In this case we skip over the file
* and mark it as removed in the database. (This behaviour differs from the desktop client).
* <p>
*/
public Pair<File, List<String>> mediaChangesZip() {
File f = new File(mCol.getPath().replaceFirst("collection\\.anki2$", "tmpSyncToServer.zip"));
Cursor cur = null;
try (ZipOutputStream z = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)))) {
z.setMethod(ZipOutputStream.DEFLATED);
List<String> fnames = new ArrayList<>();
// meta is a list of (fname, zipname), where zipname of null is a deleted file
// NOTE: In python, meta is a list of tuples that then gets serialized into json and added
// to the zip as a string. In our version, we use JSON objects from the start to avoid the
// serialization step. Instead of a list of tuples, we use JSONArrays of JSONArrays.
JSONArray meta = new JSONArray();
int sz = 0;
byte[] buffer = new byte[2048];
cur = mDb.getDatabase().query("select fname, csum from media where dirty=1 limit " + Consts.SYNC_ZIP_COUNT, null);
for (int c = 0; cur.moveToNext(); c++) {
String fname = cur.getString(0);
String csum = cur.getString(1);
fnames.add(fname);
String normname = Utils.nfcNormalized(fname);
if (!TextUtils.isEmpty(csum)) {
try {
mCol.log("+media zip " + fname);
File file = new File(dir(), fname);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), 2048);
z.putNextEntry(new ZipEntry(Integer.toString(c)));
int count = 0;
while ((count = bis.read(buffer, 0, 2048)) != -1) {
z.write(buffer, 0, count);
}
z.closeEntry();
bis.close();
meta.put(new JSONArray().put(normname).put(Integer.toString(c)));
sz += file.length();
} catch (FileNotFoundException e) {
// A file has been marked as added but no longer exists in the media directory.
// Skip over it and mark it as removed in the db.
removeFile(fname);
}
} else {
mCol.log("-media zip " + fname);
meta.put(new JSONArray().put(normname).put(""));
}
if (sz >= Consts.SYNC_ZIP_SIZE) {
break;
}
}
z.putNextEntry(new ZipEntry("_meta"));
z.write(Utils.jsonToString(meta).getBytes());
z.closeEntry();
// Don't leave lingering temp files if the VM terminates.
f.deleteOnExit();
return new Pair<>(f, fnames);
} catch (IOException e) {
Timber.e(e, "Failed to create media changes zip: ");
throw new RuntimeException(e);
} finally {
if (cur != null) {
cur.close();
}
}
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class SchedV2 method _moveToDyn.
protected void _moveToDyn(long did, @NonNull List<Long> ids, int start) {
Deck deck = mCol.getDecks().get(did);
ArrayList<Object[]> data = new ArrayList<>();
int u = mCol.usn();
int due = start;
for (Long id : ids) {
data.add(new Object[] { did, due, u, id });
due += 1;
}
String queue = "";
if (!deck.getBoolean("resched")) {
queue = ", queue = " + Consts.QUEUE_TYPE_REV + "";
}
mCol.getDb().executeMany("UPDATE cards SET odid = did, " + "odue = due, did = ?, due = (case when due <= 0 then due else ? end), usn = ? " + queue + " WHERE id = ?", data);
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class SchedV2 method _fillDyn.
/**
* Whether the filtered deck is empty
* Overriden
*/
private int _fillDyn(Deck deck) {
int start = -100000;
int total = 0;
JSONArray terms;
List<Long> ids;
terms = deck.getJSONArray("terms");
for (int i = 0; i < terms.length(); i++) {
JSONArray term = terms.getJSONArray(i);
String search = term.getString(0);
int limit = term.getInt(1);
int order = term.getInt(2);
String orderlimit = _dynOrder(order, limit);
if (!TextUtils.isEmpty(search.trim())) {
search = String.format(Locale.US, "(%s)", search);
}
search = String.format(Locale.US, "%s -is:suspended -is:buried -deck:filtered", search);
ids = mCol.findCards(search, orderlimit);
if (ids.isEmpty()) {
return total;
}
// move the cards over
mCol.log(deck.getLong("id"), ids);
_moveToDyn(deck.getLong("id"), ids, start + total);
total += ids.size();
}
return total;
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class TemporaryModelTest method testAddDeleteTracking.
@Test
public void testAddDeleteTracking() {
// Assume you start with a 2 template model (like "Basic (and reversed)")
// Add a 3rd new template, remove the 2nd, remove the 1st, add a new now-2nd, remove 1st again
// ...and it should reduce to just removing the original 1st/2nd and adding the final as first
TemporaryModel tempModel = new TemporaryModel(new Model("{ foo: bar }"));
tempModel.addTemplateChange(ADD, 3);
Object[][] expected1 = { { 3, ADD } };
// 3 templates and one change now
assertTemplateChangesEqual(expected1, tempModel.getTemplateChanges());
assertTemplateChangesEqual(expected1, tempModel.getAdjustedTemplateChanges());
Assert.assertArrayEquals(new int[] { 3 }, tempModel.getDeleteDbOrds(3));
tempModel.addTemplateChange(DELETE, 2);
// 2 templates and two changes now
Object[][] expected2 = { { 3, ADD }, { 2, DELETE } };
Object[][] adjExpected2 = { { 2, ADD }, { 2, DELETE } };
assertTemplateChangesEqual(expected2, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected2, tempModel.getAdjustedTemplateChanges());
Assert.assertArrayEquals(new int[] { 2, 4 }, tempModel.getDeleteDbOrds(3));
tempModel.addTemplateChange(DELETE, 1);
// 1 template and three changes now
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected3 = { { 3, ADD }, { 2, DELETE }, { 1, DELETE } };
Object[][] adjExpected3 = { { 1, ADD }, { 2, DELETE }, { 1, DELETE } };
assertTemplateChangesEqual(expected3, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected3, tempModel.getAdjustedTemplateChanges());
tempModel.addTemplateChange(ADD, 2);
// 2 templates and 4 changes now
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected4 = { { 3, ADD }, { 2, DELETE }, { 1, DELETE }, { 2, ADD } };
Object[][] adjExpected4 = { { 1, ADD }, { 2, DELETE }, { 1, DELETE }, { 2, ADD } };
assertTemplateChangesEqual(expected4, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected4, tempModel.getAdjustedTemplateChanges());
// Make sure we can resurrect these changes across lifecycle
Bundle outBundle = tempModel.toBundle();
assertTemplateChangesEqual(expected4, outBundle.getSerializable("mTemplateChanges"));
// This is the hard part. We will delete a template we added so everything shifts.
// The template currently at ordinal 1 was added as template 3 at the start before it slid down on the deletes
// So the first template add should be negated by this delete, and the second template add should slide down to 1
tempModel.addTemplateChange(DELETE, 1);
// 1 template and 3 changes now (the delete just cancelled out one of the adds)
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected5 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD } };
Object[][] adjExpected5 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD } };
assertTemplateChangesEqual(expected5, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected5, tempModel.getAdjustedTemplateChanges());
tempModel.addTemplateChange(ADD, 2);
// 2 template and 4 changes now (the delete just cancelled out one of the adds)
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected6 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
Object[][] adjExpected6 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
assertTemplateChangesEqual(expected6, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected6, tempModel.getAdjustedTemplateChanges());
tempModel.addTemplateChange(ADD, 3);
// 2 template and 4 changes now (the delete just cancelled out one of the adds)
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected7 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD }, { 3, ADD } };
Object[][] adjExpected7 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD }, { 3, ADD } };
assertTemplateChangesEqual(expected7, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected7, tempModel.getAdjustedTemplateChanges());
tempModel.addTemplateChange(DELETE, 3);
// 1 template and 3 changes now (two deletes cancelled out adds)
Assert.assertArrayEquals(new int[] { 2, 1, 5 }, tempModel.getDeleteDbOrds(3));
Object[][] expected8 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
Object[][] adjExpected8 = { { 2, DELETE }, { 1, DELETE }, { 1, ADD }, { 2, ADD } };
assertTemplateChangesEqual(expected8, tempModel.getTemplateChanges());
assertTemplateChangesEqual(adjExpected8, tempModel.getAdjustedTemplateChanges());
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class TemporaryModelTest method testTempModelStorage.
@Test
public void testTempModelStorage() throws Exception {
// Start off with clean state in the cache dir
TemporaryModel.clearTempModelFiles();
// Make sure save / retrieve works
String tempModelPath = TemporaryModel.saveTempModel(getTargetContext(), new JSONObject("{foo: bar}"));
Assert.assertNotNull("Saving temp model unsuccessful", tempModelPath);
JSONObject tempModel = TemporaryModel.getTempModel(tempModelPath);
Assert.assertNotNull("Temp model not read successfully", tempModel);
Assert.assertEquals(new JSONObject("{foo: bar}").toString(), tempModel.toString());
// Make sure clearing works
Assert.assertEquals(1, TemporaryModel.clearTempModelFiles());
Timber.i("The following logged NoSuchFileException is an expected part of verifying a file delete.");
try {
TemporaryModel.getTempModel(tempModelPath);
Assert.fail("Should have caught an exception here because the file is missing");
} catch (IOException e) {
// this is expected
}
}
Aggregations