use of com.ichi2.libanki.importer.AnkiPackageImporter in project AnkiChinaAndroid by ankichinateam.
the class ImportTest method testApkg.
@Test
public void testApkg() throws IOException, ImportExportException {
List<String> expected;
List<String> actual;
String apkg = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "media.apkg");
Importer imp = new AnkiPackageImporter(testCol, apkg);
expected = Collections.emptyList();
actual = Arrays.asList(new File(testCol.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(actual.size(), expected.size());
imp.run();
expected = Collections.singletonList("foo.wav");
actual = Arrays.asList(new File(testCol.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
// import again should be idempotent in terms of media
testCol.remCards(testCol.getDb().queryLongList("select id from cards"));
imp = new AnkiPackageImporter(testCol, apkg);
imp.run();
expected = Collections.singletonList("foo.wav");
actual = Arrays.asList(new File(testCol.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
// but if the local file has different data, it will rename
testCol.remCards(testCol.getDb().queryLongList("select id from cards"));
FileOutputStream os;
os = new FileOutputStream(new File(testCol.getMedia().dir(), "foo.wav"), false);
os.write("xyz".getBytes());
os.close();
imp = new AnkiPackageImporter(testCol, apkg);
imp.run();
assertEquals(2, new File(testCol.getMedia().dir()).list().length);
}
use of com.ichi2.libanki.importer.AnkiPackageImporter in project AnkiChinaAndroid by ankichinateam.
the class AnkiPackageImporter method run.
@Override
public void run() throws ImportExportException {
publishProgress(0, 0, 0);
File tempDir = new File(new File(mCol.getPath()).getParent(), "tmpzip");
// self.col into Anki.
Collection tmpCol;
Timber.d("Attempting to import package %s", mFile);
String tmpApkgPath = "";
if (mFile.endsWith(".card")) {
tmpApkgPath = mFile.replace(".card", ".apkg");
AESUtil.decryptionFile(mFile, tmpApkgPath);
mFile = tmpApkgPath;
}
try {
// We extract the zip contents into a temporary directory and do a little more
// validation than the desktop client to ensure the extracted collection is an apkg.
String colname = "collection.anki21";
try {
// extract the deck from the zip file
try {
mZip = new ZipFile(new File(mFile));
} catch (FileNotFoundException fileNotFound) {
// The cache can be cleared between copying the file in and importing. This is temporary
if (fileNotFound.getMessage().contains("ENOENT")) {
mLog.add(getRes().getString(R.string.import_log_file_cache_cleared));
return;
}
// displays: failed to unzip
throw fileNotFound;
}
// v2 scheduler?
if (mZip.getEntry(colname) == null) {
colname = CollectionHelper.COLLECTION_FILENAME;
}
// Make sure we have sufficient free space
long uncompressedSize = Utils.calculateUncompressedSize(mZip);
long availableSpace = Utils.determineBytesAvailable(mCol.getPath());
Timber.d("Total uncompressed size will be: %d", uncompressedSize);
Timber.d("Total available size is: %d", availableSpace);
if (uncompressedSize > availableSpace) {
Timber.e("Not enough space to unzip, need %d, available %d", uncompressedSize, availableSpace);
mLog.add(getRes().getString(R.string.import_log_insufficient_space, uncompressedSize, availableSpace));
return;
}
// The filename that we extract should be collection.anki2
// Importing collection.anki21 fails due to some media regexes expecting collection.anki2.
// We follow how Anki does it and fix the problem here.
HashMap<String, String> mediaToFileNameMap = new HashMap<>();
mediaToFileNameMap.put(colname, CollectionHelper.COLLECTION_FILENAME);
Utils.unzipFiles(mZip, tempDir.getAbsolutePath(), new String[] { colname, "media" }, mediaToFileNameMap);
colname = CollectionHelper.COLLECTION_FILENAME;
} catch (IOException e) {
Timber.e(e, "Failed to unzip apkg.");
AnkiDroidApp.sendExceptionReport(e, "AnkiPackageImporter::run() - unzip");
mLog.add(getRes().getString(R.string.import_log_failed_unzip, e.getLocalizedMessage()));
return;
}
String colpath = new File(tempDir, colname).getAbsolutePath();
if (!(new File(colpath)).exists()) {
mLog.add(getRes().getString(R.string.import_log_failed_copy_to, colpath));
return;
}
tmpCol = Storage.Collection(mContext, colpath);
try {
if (!tmpCol.validCollection()) {
mLog.add(getRes().getString(R.string.import_log_failed_validate));
return;
}
} finally {
if (tmpCol != null) {
tmpCol.close();
}
}
mFile = colpath;
// we need the media dict in advance, and we'll need a map of fname ->
// number to use during the import
File mediaMapFile = new File(tempDir, "media");
mNameToNum = new HashMap<>();
String dirPath = tmpCol.getMedia().dir();
File dir = new File(dirPath);
// We need the opposite mapping in AnkiDroid since our extraction method requires it.
Map<String, String> numToName = new HashMap<>();
try (JsonReader jr = new JsonReader(new FileReader(mediaMapFile))) {
jr.beginObject();
// v in anki
String name;
// k in anki
String num;
while (jr.hasNext()) {
num = jr.nextName();
name = jr.nextString();
File file = new File(dir, name);
if (!Utils.isInside(file, dir)) {
throw (new RuntimeException("Invalid file"));
}
Utils.nfcNormalized(num);
mNameToNum.put(name, num);
numToName.put(num, name);
}
jr.endObject();
} catch (FileNotFoundException e) {
Timber.e("Apkg did not contain a media dict. No media will be imported.");
} catch (IOException e) {
Timber.e("Malformed media dict. Media import will be incomplete.");
}
// run anki2 importer
super.run();
// import static media
for (Map.Entry<String, String> entry : mNameToNum.entrySet()) {
String file = entry.getKey();
String c = entry.getValue();
if (!file.startsWith("_") && !file.startsWith("latex-")) {
continue;
}
File path = new File(mCol.getMedia().dir(), Utils.nfcNormalized(file));
if (!path.exists()) {
try {
Utils.unzipFiles(mZip, mCol.getMedia().dir(), new String[] { c }, numToName);
} catch (IOException e) {
Timber.e("Failed to extract static media file. Ignoring.");
}
}
}
} finally {
long availableSpace = Utils.determineBytesAvailable(mCol.getPath());
Timber.d("Total available size is: %d", availableSpace);
// Clean up our temporary files
if (tempDir.exists()) {
BackupManager.removeDir(tempDir);
}
}
publishProgress(100, 100, 100);
// if(!tmpApkgPath.isEmpty()){
// new File(tmpApkgPath).delete();
// }
}
use of com.ichi2.libanki.importer.AnkiPackageImporter in project Anki-Android by ankidroid.
the class ImportTest method testAnki2Updates.
@Test
public void testAnki2Updates() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(getTestContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(0, imp.getDupes());
assertEquals(1, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing again should be idempotent
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing a newer note should update
assertEquals(1, mTestCol.noteCount());
assertTrue(mTestCol.getDb().queryString("select flds from notes").startsWith("hello"));
tmp = Shared.getTestFilePath(getTestContext(), "update2.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(1, imp.getUpdated());
assertTrue(mTestCol.getDb().queryString("select flds from notes").startsWith("goodbye"));
}
use of com.ichi2.libanki.importer.AnkiPackageImporter in project Anki-Android by ankidroid.
the class ImportTest method testAnki2DiffmodelTemplates.
@Test
public void testAnki2DiffmodelTemplates() throws IOException, JSONException, ImportExportException {
// different from the above as this one tests only the template text being
// changed, not the number of cards/fields
// import the first version of the model
String tmp = Shared.getTestFilePath(getTestContext(), "diffmodeltemplates-1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// then the version with updated template
tmp = Shared.getTestFilePath(getTestContext(), "diffmodeltemplates-2.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.setDupeOnSchemaChange(true);
imp.run();
// collection should contain the note we imported
assertEquals(1, mTestCol.noteCount());
// the front template should contain the text added in the 2nd package
Long tcid = mTestCol.findCards("").get(0);
Note tnote = mTestCol.getCard(tcid).note();
assertTrue(mTestCol.findTemplates(tnote).get(0).getString("qfmt").contains("Changed Front Template"));
}
use of com.ichi2.libanki.importer.AnkiPackageImporter in project Anki-Android by ankidroid.
the class ImportTest method testDupeIgnore.
/**
* Custom tests for AnkiDroid.
*/
@Test
public void testDupeIgnore() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(getTestContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
tmp = Shared.getTestFilePath(getTestContext(), "update3.apkg");
imp = new AnkiPackageImporter(mTestCol, tmp);
imp.run();
// there is a dupe, but it was ignored
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
}
Aggregations