Search in sources :

Example 1 with AnkiDb

use of com.ichi2.anki.AnkiDb in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundUpgradeDecks.

private Payload doInBackgroundUpgradeDecks(Payload data) {
    // Enable http request canceller
    mCancelCallback = new CancelCallback();
    String path = (String) data.data[0];
    File ankiDir = new File(path);
    if (!ankiDir.isDirectory()) {
        data.success = false;
        data.data = new Object[] { "wrong anki directory" };
        return data;
    }
    // step 1: gather all .anki files into a zip, without media.
    // we must store them as 1.anki, 2.anki and provide a map so we don't run into
    // encoding issues with the zip file.
    File[] fileList = ankiDir.listFiles(new OldAnkiDeckFilter());
    List<String> corruptFiles = new ArrayList<String>();
    JSONObject map = new JSONObject();
    byte[] buf = new byte[1024];
    String zipFilename = path + "/upload.zip";
    String colFilename = path + AnkiDroidApp.COLLECTION_PATH;
    try {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilename));
        int n = 1;
        for (File f : fileList) {
            String deckPath = f.getAbsolutePath();
            // set journal mode to delete
            try {
                AnkiDb d = AnkiDatabaseManager.getDatabase(deckPath);
            } catch (SQLiteDatabaseCorruptException e) {
                // ignore invalid .anki files
                corruptFiles.add(f.getName());
                continue;
            } finally {
                AnkiDatabaseManager.closeDatabase(deckPath);
            }
            // zip file
            String tmpName = n + ".anki";
            FileInputStream in = new FileInputStream(deckPath);
            ZipEntry ze = new ZipEntry(tmpName);
            zos.putNextEntry(ze);
            int len;
            while ((len = in.read(buf)) >= 0) {
                zos.write(buf, 0, len);
            }
            zos.closeEntry();
            map.put(tmpName, f.getName());
            n++;
        }
        // if all .anki files were found corrupted, abort
        if (fileList.length == corruptFiles.size()) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        ZipEntry ze = new ZipEntry("map.json");
        zos.putNextEntry(ze);
        InputStream in = new ByteArrayInputStream(Utils.jsonToString(map).getBytes("UTF-8"));
        int len;
        while ((len = in.read(buf)) >= 0) {
            zos.write(buf, 0, len);
        }
        zos.closeEntry();
        zos.close();
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (JSONException e) {
        throw new RuntimeException(e);
    }
    File zipFile = new File(zipFilename);
    // step 1.1: if it's over 50MB compressed, it must be upgraded by the user
    if (zipFile.length() > 50 * 1024 * 1024) {
        data.success = false;
        data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_exceeds) };
        return data;
    }
    // step 2: upload zip file to upgrade service and get token
    BasicHttpSyncer h = new BasicHttpSyncer(null, null);
    // note: server doesn't expect it to be gzip compressed, because the zip file is compressed
    // enable cancelling
    publishProgress(R.string.upgrade_decks_upload, null, true);
    try {
        HttpResponse resp = h.req("upgrade/upload", new FileInputStream(zipFile), 0, false, null, mCancelCallback);
        if (resp == null && !isCancelled()) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        String result;
        String key = null;
        if (!isCancelled()) {
            result = h.stream2String(resp.getEntity().getContent());
            if (result != null && result.startsWith("ok:")) {
                key = result.split(":")[1];
            } else {
                data.success = false;
                data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
                return data;
            }
        }
        while (!isCancelled()) {
            result = h.stream2String(h.req("upgrade/status?key=" + key).getEntity().getContent());
            if (result.equals("error")) {
                data.success = false;
                data.data = new Object[] { "error" };
                return data;
            } else if (result.startsWith("waiting:")) {
                publishProgress(R.string.upgrade_decks_upload, result.split(":")[1]);
            } else if (result.equals("upgrading")) {
                publishProgress(new Object[] { R.string.upgrade_decks_upgrade_started });
            } else if (result.equals("ready")) {
                break;
            } else {
                data.success = false;
                data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
                return data;
            }
            Thread.sleep(1000);
        }
        // gzip compression if the client says it can handle it
        if (!isCancelled()) {
            publishProgress(new Object[] { R.string.upgrade_decks_downloading });
            resp = h.req("upgrade/download?key=" + key, null, 6, true, null, mCancelCallback);
        // uploads/downloads have finished so disable cancelling
        }
        publishProgress(R.string.upgrade_decks_downloading, null, false);
        if (isCancelled()) {
            return null;
        }
        if (resp == null) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        // step 5: check the received file is valid
        InputStream cont = resp.getEntity().getContent();
        if (!h.writeToFile(cont, colFilename)) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_sdcard, new File(colFilename).length() / 1048576 + 1) };
            (new File(colFilename)).delete();
            return data;
        }
        // check the received file is ok
        publishProgress(new Object[] { R.string.sync_check_download_file });
        publishProgress(R.string.sync_check_download_file);
        try {
            AnkiDb d = AnkiDatabaseManager.getDatabase(colFilename);
            if (!d.queryString("PRAGMA integrity_check").equalsIgnoreCase("ok")) {
                data.success = false;
                data.data = new Object[] { sContext.getResources() };
                return data;
            }
        } finally {
            AnkiDatabaseManager.closeDatabase(colFilename);
        }
        Collection col = AnkiDroidApp.openCollection(colFilename);
        ArrayList<String> decks = col.getDecks().allNames(false);
        ArrayList<String> failed = new ArrayList<String>();
        ArrayList<File> mediaDirs = new ArrayList<File>();
        for (File f : fileList) {
            String name = f.getName().replaceFirst("\\.anki$", "");
            if (!decks.contains(name)) {
                failed.add(name);
            } else {
                mediaDirs.add(new File(f.getAbsolutePath().replaceFirst("\\.anki$", ".media")));
            }
        }
        File newMediaDir = new File(col.getMedia().getDir());
        // step 6. move media files to new media directory
        publishProgress(new Object[] { R.string.upgrade_decks_media });
        ArrayList<String> failedMedia = new ArrayList<String>();
        File curMediaDir = null;
        for (File mediaDir : mediaDirs) {
            curMediaDir = mediaDir;
            // Check if media directory exists and is local
            if (!curMediaDir.exists() || !curMediaDir.isDirectory()) {
                // If not try finding it in dropbox 1.2.x
                curMediaDir = new File(AnkiDroidApp.getDropboxDir(), mediaDir.getName());
                if (!curMediaDir.exists() || !curMediaDir.isDirectory()) {
                    // No media for this deck
                    continue;
                }
            }
            // Found media dir, copy files
            for (File m : curMediaDir.listFiles()) {
                try {
                    Utils.copyFile(m, new File(newMediaDir, m.getName()));
                } catch (IOException e) {
                    failedMedia.add(curMediaDir.getName().replaceFirst("\\.media$", ".anki"));
                    break;
                }
            }
        }
        data.data = new Object[] { failed, failedMedia, newMediaDir.getAbsolutePath() };
        data.success = true;
        return data;
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } catch (IllegalStateException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        (new File(zipFilename)).delete();
    }
}
Also used : ZipEntry(java.util.zip.ZipEntry) ArrayList(java.util.ArrayList) FileNotFoundException(java.io.FileNotFoundException) SQLiteDatabaseCorruptException(android.database.sqlite.SQLiteDatabaseCorruptException) AnkiDb(com.ichi2.anki.AnkiDb) BufferedInputStream(java.io.BufferedInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) JSONException(org.json.JSONException) HttpResponse(org.apache.http.HttpResponse) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) BasicHttpSyncer(com.ichi2.libanki.sync.BasicHttpSyncer) JSONObject(org.json.JSONObject) ByteArrayInputStream(java.io.ByteArrayInputStream) ZipOutputStream(java.util.zip.ZipOutputStream) FileOutputStream(java.io.FileOutputStream) Collection(com.ichi2.libanki.Collection) JSONObject(org.json.JSONObject) File(java.io.File)

Example 2 with AnkiDb

use of com.ichi2.anki.AnkiDb in project Anki-Android by Ramblurr.

the class DeckTask method doInBackgroundExportApkg.

private TaskData doInBackgroundExportApkg(TaskData... params) {
    Log.i(AnkiDroidApp.TAG, "doInBackgroundExportApkg");
    Object[] data = params[0].getObjArray();
    String colPath = (String) data[0];
    String apkgPath = (String) data[1];
    boolean includeMedia = (Boolean) data[2];
    byte[] buf = new byte[1024];
    try {
        try {
            AnkiDb d = AnkiDatabaseManager.getDatabase(colPath);
        } catch (SQLiteDatabaseCorruptException e) {
            // collection is invalid
            return new TaskData(false);
        } finally {
            AnkiDatabaseManager.closeDatabase(colPath);
        }
        // export collection
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(apkgPath));
        FileInputStream colFin = new FileInputStream(colPath);
        ZipEntry ze = new ZipEntry("collection.anki2");
        zos.putNextEntry(ze);
        int len;
        while ((len = colFin.read(buf)) >= 0) {
            zos.write(buf, 0, len);
        }
        zos.closeEntry();
        colFin.close();
        // export media
        JSONObject media = new JSONObject();
        if (includeMedia) {
            File mediaDir = new File(AnkiDroidApp.getCurrentAnkiDroidMediaDir());
            if (mediaDir.exists() && mediaDir.isDirectory()) {
                File[] mediaFiles = mediaDir.listFiles();
                int c = 0;
                for (File f : mediaFiles) {
                    FileInputStream mediaFin = new FileInputStream(f);
                    ze = new ZipEntry(Integer.toString(c));
                    zos.putNextEntry(ze);
                    while ((len = mediaFin.read(buf)) >= 0) {
                        zos.write(buf, 0, len);
                    }
                    zos.closeEntry();
                    media.put(Integer.toString(c), f.getName());
                }
            }
        }
        ze = new ZipEntry("media");
        zos.putNextEntry(ze);
        InputStream mediaIn = new ByteArrayInputStream(Utils.jsonToString(media).getBytes("UTF-8"));
        while ((len = mediaIn.read(buf)) >= 0) {
            zos.write(buf, 0, len);
        }
        zos.closeEntry();
        zos.close();
    } catch (FileNotFoundException e) {
        return new TaskData(false);
    } catch (IOException e) {
        return new TaskData(false);
    } catch (JSONException e) {
        return new TaskData(false);
    }
    return new TaskData(true);
}
Also used : AnkiDb(com.ichi2.anki.AnkiDb) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) ZipEntry(java.util.zip.ZipEntry) FileNotFoundException(java.io.FileNotFoundException) SQLiteDatabaseCorruptException(android.database.sqlite.SQLiteDatabaseCorruptException) JSONException(org.json.JSONException) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) JSONObject(org.json.JSONObject) ByteArrayInputStream(java.io.ByteArrayInputStream) ZipOutputStream(java.util.zip.ZipOutputStream) FileOutputStream(java.io.FileOutputStream) JSONObject(org.json.JSONObject) ZipFile(java.util.zip.ZipFile) File(java.io.File)

Example 3 with AnkiDb

use of com.ichi2.anki.AnkiDb in project Anki-Android by Ramblurr.

the class DeckTask method doInBackgroundImportAdd.

private TaskData doInBackgroundImportAdd(TaskData... params) {
    Log.i(AnkiDroidApp.TAG, "doInBackgroundImportAdd");
    Resources res = AnkiDroidApp.getInstance().getBaseContext().getResources();
    Collection col = params[0].getCollection();
    String path = params[0].getString();
    boolean sharedDeckImport = params[0].getBoolean();
    ProgressCallback pc = null;
    // don't report progress on shared deck import (or maybe should we?)
    if (!sharedDeckImport) {
        pc = new ProgressCallback(this, res);
    }
    int addedCount = -1;
    try {
        Anki2Importer imp = new Anki2Importer(col, path, pc);
        AnkiDb ankiDB = col.getDb();
        ankiDB.getDatabase().beginTransaction();
        try {
            addedCount = imp.run();
            ankiDB.getDatabase().setTransactionSuccessful();
        } finally {
            ankiDB.getDatabase().endTransaction();
            if (sharedDeckImport) {
                File tmpFile = new File(path);
                tmpFile.delete();
            }
        }
        if (addedCount >= 0) {
            ankiDB.execute("VACUUM");
            ankiDB.execute("ANALYZE");
        }
        publishProgress(new TaskData(res.getString(R.string.import_update_counts)));
        // Update the counts
        DeckTask.TaskData result = doInBackgroundLoadDeckCounts(new TaskData(col));
        if (result == null) {
            return null;
        }
        return new TaskData(addedCount, result.getObjArray(), true);
    } catch (RuntimeException e) {
        Log.e(AnkiDroidApp.TAG, "doInBackgroundImportAdd - RuntimeException on importing cards: ", e);
        AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundImportAdd");
        return new TaskData(false);
    } catch (IOException e) {
        Log.e(AnkiDroidApp.TAG, "doInBackgroundImportAdd - IOException on importing cards: ", e);
        AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundImportAdd");
        return new TaskData(false);
    }
}
Also used : Anki2Importer(com.ichi2.libanki.importer.Anki2Importer) AnkiDb(com.ichi2.anki.AnkiDb) IOException(java.io.IOException) Collection(com.ichi2.libanki.Collection) Resources(android.content.res.Resources) ZipFile(java.util.zip.ZipFile) File(java.io.File)

Example 4 with AnkiDb

use of com.ichi2.anki.AnkiDb in project Anki-Android by Ramblurr.

the class FullSyncer method download.

@Override
public Object[] download() {
    InputStream cont;
    try {
        HttpResponse ret = super.req("download");
        if (ret == null) {
            return null;
        }
        cont = ret.getEntity().getContent();
    } catch (IllegalStateException e1) {
        throw new RuntimeException(e1);
    } catch (IOException e1) {
        return null;
    }
    String path = AnkiDroidApp.getCollectionPath();
    if (mCol != null) {
        mCol.close(false);
        mCol = null;
    }
    String tpath = path + ".tmp";
    if (!super.writeToFile(cont, tpath)) {
        return new Object[] { "sdAccessError" };
    }
    // first check, if account needs upgrade (from 1.2)
    try {
        FileInputStream fis = new FileInputStream(tpath);
        if (super.stream2String(fis, 15).equals("upgradeRequired")) {
            return new Object[] { "upgradeRequired" };
        }
    } catch (FileNotFoundException e1) {
        throw new RuntimeException(e1);
    }
    // check the received file is ok
    mCon.publishProgress(R.string.sync_check_download_file);
    try {
        AnkiDb d = AnkiDatabaseManager.getDatabase(tpath);
        if (!d.queryString("PRAGMA integrity_check").equalsIgnoreCase("ok")) {
            Log.e(AnkiDroidApp.TAG, "Full sync - downloaded file corrupt");
            return new Object[] { "remoteDbError" };
        }
    } catch (SQLiteDatabaseCorruptException e) {
        Log.e(AnkiDroidApp.TAG, "Full sync - downloaded file corrupt");
        return new Object[] { "remoteDbError" };
    } finally {
        AnkiDatabaseManager.closeDatabase(tpath);
    }
    // overwrite existing collection
    File newFile = new File(tpath);
    if (newFile.renameTo(new File(path))) {
        return new Object[] { "success" };
    } else {
        return new Object[] { "overwriteError" };
    }
}
Also used : AnkiDb(com.ichi2.anki.AnkiDb) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) FileNotFoundException(java.io.FileNotFoundException) HttpResponse(org.apache.http.HttpResponse) SQLiteDatabaseCorruptException(android.database.sqlite.SQLiteDatabaseCorruptException) IOException(java.io.IOException) File(java.io.File) FileInputStream(java.io.FileInputStream)

Example 5 with AnkiDb

use of com.ichi2.anki.AnkiDb in project Anki-Android by Ramblurr.

the class Storage method Collection.

public static Collection Collection(String path, boolean server) {
    assert path.endsWith(".anki2");
    File dbFile = new File(path);
    boolean create = !dbFile.exists();
    if (create) {
        AnkiDroidApp.createDirectoryIfMissing(dbFile.getParentFile());
    }
    // connect
    AnkiDb db = AnkiDatabaseManager.getDatabase(path);
    int ver;
    if (create) {
        ver = _createDB(db);
    } else {
        ver = _upgradeSchema(db);
    }
    db.execute("PRAGMA temp_store = memory");
    // LIBANKI: sync, journal_mode --> in AnkiDroid done in AnkiDb
    // add db to col and do any remaining upgrades
    Collection col = new Collection(db, path, server);
    if (ver < Collection.SCHEMA_VERSION) {
        _upgrade(col, ver);
    } else if (create) {
        // add in reverse order so basic is default
        Models.addClozeModel(col);
        Models.addForwardOptionalReverse(col);
        Models.addForwardReverse(col);
        Models.addBasicModel(col);
        col.save();
    }
    return col;
}
Also used : AnkiDb(com.ichi2.anki.AnkiDb) File(java.io.File)

Aggregations

AnkiDb (com.ichi2.anki.AnkiDb)9 File (java.io.File)5 IOException (java.io.IOException)4 SQLiteDatabaseCorruptException (android.database.sqlite.SQLiteDatabaseCorruptException)3 Collection (com.ichi2.libanki.Collection)3 FileInputStream (java.io.FileInputStream)3 FileNotFoundException (java.io.FileNotFoundException)3 InputStream (java.io.InputStream)3 Card (com.ichi2.libanki.Card)2 Note (com.ichi2.libanki.Note)2 Sched (com.ichi2.libanki.Sched)2 ByteArrayInputStream (java.io.ByteArrayInputStream)2 FileOutputStream (java.io.FileOutputStream)2 ZipEntry (java.util.zip.ZipEntry)2 ZipFile (java.util.zip.ZipFile)2 ZipOutputStream (java.util.zip.ZipOutputStream)2 HttpResponse (org.apache.http.HttpResponse)2 JSONException (org.json.JSONException)2 JSONObject (org.json.JSONObject)2 Resources (android.content.res.Resources)1