Search in sources :

Example 26 with DB

use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.

the class Media method rebuildIfInvalid.

public void rebuildIfInvalid() throws IOException {
    try {
        _changed();
        return;
    } catch (Exception e) {
        if (!ExceptionUtil.containsMessage(e, "no such table: meta")) {
            throw e;
        }
        AnkiDroidApp.sendExceptionReport(e, "media::rebuildIfInvalid");
        // TODO: We don't know the root cause of the missing meta table
        Timber.w(e, "Error accessing media database. Rebuilding");
    // continue below
    }
    // Delete and recreate the file
    mDb.getDatabase().close();
    String path = mDb.getPath();
    Timber.i("Deleted %s", path);
    new File(path).delete();
    mDb = new DB(path);
    _initDB();
}
Also used : ZipFile(java.util.zip.ZipFile) File(java.io.File) EmptyMediaException(com.ichi2.libanki.exception.EmptyMediaException) IOException(java.io.IOException) SQLException(android.database.SQLException) FileNotFoundException(java.io.FileNotFoundException)

Example 27 with DB

use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.

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 directory 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"));
    List<String> fnames = new ArrayList<>();
    try (ZipOutputStream z = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
        Cursor cur = mDb.query("select fname, csum from media where dirty=1 limit " + Consts.SYNC_MAX_FILES)) {
        z.setMethod(ZipOutputStream.DEFLATED);
        // 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];
        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) {
                    Timber.w(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_MAX_BYTES) {
                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);
    }
}
Also used : ZipEntry(java.util.zip.ZipEntry) ArrayList(java.util.ArrayList) JSONArray(com.ichi2.utils.JSONArray) FileNotFoundException(java.io.FileNotFoundException) IOException(java.io.IOException) Cursor(android.database.Cursor) FileInputStream(java.io.FileInputStream) BufferedInputStream(java.io.BufferedInputStream) ZipOutputStream(java.util.zip.ZipOutputStream) FileOutputStream(java.io.FileOutputStream) ZipFile(java.util.zip.ZipFile) File(java.io.File) BufferedOutputStream(java.io.BufferedOutputStream) Pair(android.util.Pair)

Example 28 with DB

use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.

the class Media method maybeUpgrade.

public void maybeUpgrade() {
    String oldpath = dir() + ".db";
    File oldDbFile = new File(oldpath);
    if (oldDbFile.exists()) {
        mDb.execute(String.format(Locale.US, "attach \"%s\" as old", oldpath));
        try {
            String sql = "insert into media\n" + " select m.fname, csum, mod, ifnull((select 1 from log l2 where l2.fname=m.fname), 0) as dirty\n" + " from old.media m\n" + " left outer join old.log l using (fname)\n" + " union\n" + " select fname, null, 0, 1 from old.log where type=" + Consts.CARD_TYPE_LRN + ";";
            mDb.execute(sql);
            mDb.execute("delete from meta");
            mDb.execute("insert into meta select dirMod, usn from old.meta");
            mDb.commit();
        } catch (Exception e) {
            // if we couldn't import the old db for some reason, just start anew
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            mCol.log("failed to import old media db:" + sw.toString());
        }
        mDb.execute("detach old");
        File newDbFile = new File(oldpath + ".old");
        if (newDbFile.exists()) {
            newDbFile.delete();
        }
        oldDbFile.renameTo(newDbFile);
    }
}
Also used : StringWriter(java.io.StringWriter) ZipFile(java.util.zip.ZipFile) File(java.io.File) EmptyMediaException(com.ichi2.libanki.exception.EmptyMediaException) IOException(java.io.IOException) SQLException(android.database.SQLException) FileNotFoundException(java.io.FileNotFoundException) PrintWriter(java.io.PrintWriter)

Example 29 with DB

use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.

the class Storage method getDatabaseVersion.

/**
 * Helper method for when the collection can't be opened
 */
public static int getDatabaseVersion(String path) throws UnknownDatabaseVersionException {
    try {
        if (!new File(path).exists()) {
            throw new UnknownDatabaseVersionException(new FileNotFoundException(path));
        }
        DB db = new DB(path);
        int result = db.queryScalar("SELECT ver FROM col");
        db.close();
        return result;
    } catch (Exception e) {
        Timber.w(e, "Can't open database");
        throw new UnknownDatabaseVersionException(e);
    }
}
Also used : UnknownDatabaseVersionException(com.ichi2.libanki.exception.UnknownDatabaseVersionException) FileNotFoundException(java.io.FileNotFoundException) File(java.io.File) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) FileNotFoundException(java.io.FileNotFoundException) JSONException(com.ichi2.utils.JSONException) UnknownDatabaseVersionException(com.ichi2.libanki.exception.UnknownDatabaseVersionException)

Example 30 with DB

use of com.ichi2.libanki.DB in project Anki-Android by ankidroid.

the class FullSyncer method download.

@NonNull
public ConnectionResultType download() throws UnknownHttpResponseException {
    InputStream cont;
    ResponseBody body = null;
    try {
        Response ret = super.req("download");
        if (ret == null || ret.body() == null) {
            return null;
        }
        body = ret.body();
        cont = body.byteStream();
    } catch (IllegalArgumentException e1) {
        if (body != null) {
            body.close();
        }
        throw new RuntimeException(e1);
    }
    String path;
    if (mCol != null) {
        Timber.i("Closing collection for full sync");
        // Usual case where collection is non-null
        path = mCol.getPath();
        mCol.close();
        mCol = null;
    } else {
        // Allow for case where collection is completely unreadable
        Timber.w("Collection was unexpectedly null when doing full sync download");
        path = CollectionHelper.getCollectionPath(AnkiDroidApp.getInstance());
    }
    String tpath = path + ".tmp";
    try {
        super.writeToFile(cont, tpath);
        Timber.d("Full Sync - Downloaded temp file");
        FileInputStream fis = new FileInputStream(tpath);
        if ("upgradeRequired".equals(super.stream2String(fis, 15))) {
            Timber.w("Full Sync - 'Upgrade Required' message received");
            return UPGRADE_REQUIRED;
        }
    } catch (FileNotFoundException e) {
        Timber.e(e, "Failed to create temp file when downloading collection.");
        throw new RuntimeException(e);
    } catch (IOException e) {
        Timber.e(e, "Full sync failed to download collection.");
        return SD_ACCESS_ERROR;
    } finally {
        body.close();
    }
    // check the received file is ok
    mCon.publishProgress(R.string.sync_check_download_file);
    DB tempDb = null;
    try {
        tempDb = new DB(tpath);
        if (!"ok".equalsIgnoreCase(tempDb.queryString("PRAGMA integrity_check"))) {
            Timber.e("Full sync - downloaded file corrupt");
            return REMOTE_DB_ERROR;
        }
    } catch (SQLiteDatabaseCorruptException e) {
        Timber.e("Full sync - downloaded file corrupt");
        return REMOTE_DB_ERROR;
    } finally {
        if (tempDb != null) {
            tempDb.close();
        }
    }
    Timber.d("Full Sync: Downloaded file was not corrupt");
    // overwrite existing collection
    File newFile = new File(tpath);
    if (newFile.renameTo(new File(path))) {
        Timber.i("Full Sync Success: Overwritten collection with downloaded file");
        return SUCCESS;
    } else {
        Timber.w("Full Sync: Error overwriting collection with downloaded file");
        return OVERWRITE_ERROR;
    }
}
Also used : Response(okhttp3.Response) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) FileNotFoundException(java.io.FileNotFoundException) SQLiteDatabaseCorruptException(android.database.sqlite.SQLiteDatabaseCorruptException) IOException(java.io.IOException) File(java.io.File) FileInputStream(java.io.FileInputStream) DB(com.ichi2.libanki.DB) ResponseBody(okhttp3.ResponseBody) NonNull(androidx.annotation.NonNull)

Aggregations

File (java.io.File)19 JSONObject (com.ichi2.utils.JSONObject)18 Collection (com.ichi2.libanki.Collection)14 IOException (java.io.IOException)14 DB (com.ichi2.libanki.DB)13 FileNotFoundException (java.io.FileNotFoundException)13 ArrayList (java.util.ArrayList)11 JSONArray (com.ichi2.utils.JSONArray)10 Test (org.junit.Test)10 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)9 ContentValues (android.content.ContentValues)8 Resources (android.content.res.Resources)8 Uri (android.net.Uri)8 JSONException (com.ichi2.utils.JSONException)8 Cursor (android.database.Cursor)7 Model (com.ichi2.libanki.Model)7 ContentResolver (android.content.ContentResolver)6 FileInputStream (java.io.FileInputStream)6 SQLException (android.database.SQLException)4 BufferedInputStream (java.io.BufferedInputStream)4