Search in sources :

Example 1 with Decks

use of com.ichi2.libanki.Decks in project Anki-Android by Ramblurr.

the class MetaDB method storeWidgetStatus.

/**
     * Stores the current state of the widget.
     * <p>
     * It replaces any stored state for the widget.
     * 
     * @param decks an array of {@link DeckStatus} objects, one for each of the know decks.
     */
public static void storeWidgetStatus(Context context, DeckStatus[] decks) {
    openDBIfClosed(context);
    try {
        mMetaDb.beginTransaction();
        try {
            // First clear all the existing content.
            mMetaDb.execSQL("DELETE FROM widgetStatus");
            for (DeckStatus deck : decks) {
                mMetaDb.execSQL("INSERT INTO widgetStatus(deckId, deckName, newCards, lrnCards, dueCards, progress, eta) " + "VALUES (?, ?, ?, ?, ?, ?, ?)", new Object[] { deck.mDeckId, deck.mDeckName, deck.mNewCards, deck.mLrnCards, deck.mDueCards, deck.mProgress, deck.mEta });
            }
            mMetaDb.setTransactionSuccessful();
        } finally {
            mMetaDb.endTransaction();
        }
    } catch (IllegalStateException e) {
        Log.e(AnkiDroidApp.TAG, "MetaDB.storeWidgetStatus: failed", e);
    } catch (SQLiteException e) {
        Log.e(AnkiDroidApp.TAG, "MetaDB.storeWidgetStatus: failed", e);
        closeDB();
        Log.i(AnkiDroidApp.TAG, "Trying to reset Widget: " + resetWidget(context));
    }
}
Also used : DeckStatus(com.ichi2.widget.DeckStatus) SQLiteException(android.database.sqlite.SQLiteException)

Example 2 with Decks

use of com.ichi2.libanki.Decks 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 3 with Decks

use of com.ichi2.libanki.Decks in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundDownloadMissingMedia.

/**
     * Downloads any missing media files according to the mediaURL deckvar.
     * 
     * @param data
     * @return The return type contains data.resultType and an array of Integer in data.data. data.data[0] is the number
     *         of total missing media, data.data[1] is the number of downloaded ones.
     */
private Payload doInBackgroundDownloadMissingMedia(Payload data) {
    Log.i(AnkiDroidApp.TAG, "DownloadMissingMedia");
    HashMap<String, String> missingPaths = new HashMap<String, String>();
    HashMap<String, String> missingSums = new HashMap<String, String>();
    Decks deck = (Decks) data.data[0];
    // pass it to the return object so we close the deck in the deck picker
    data.result = deck;
    // deck.getDeckName();
    String syncName = "";
    data.success = false;
    data.data = new Object[] { 0, 0, 0 };
    // if (!deck.hasKey("mediaURL")) {
    // data.success = true;
    // return data;
    // }
    // deck.getVar("mediaURL");
    String urlbase = "";
    if (urlbase.equals("")) {
        data.success = true;
        return data;
    }
    // deck.mediaDir(true);
    String mdir = "";
    int totalMissing = 0;
    int missing = 0;
    int grabbed = 0;
    Cursor cursor = null;
    try {
        // deck.getDB().getDatabase().rawQuery("SELECT filename, originalPath FROM media", null);
        cursor = null;
        String path = null;
        String f = null;
        while (cursor.moveToNext()) {
            f = cursor.getString(0);
            path = mdir + "/" + f;
            File file = new File(path);
            if (!file.exists()) {
                missingPaths.put(f, path);
                missingSums.put(f, cursor.getString(1));
                Log.i(AnkiDroidApp.TAG, "Missing file: " + f);
            }
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    totalMissing = missingPaths.size();
    data.data[0] = new Integer(totalMissing);
    if (totalMissing == 0) {
        data.success = true;
        return data;
    }
    publishProgress(Boolean.FALSE, new Integer(totalMissing), new Integer(0), syncName);
    URL url = null;
    HttpURLConnection connection = null;
    String path = null;
    String sum = null;
    int readbytes = 0;
    byte[] buf = new byte[4096];
    for (String file : missingPaths.keySet()) {
        try {
            android.net.Uri uri = android.net.Uri.parse(Uri.encode(urlbase, ":/@%") + Uri.encode(file));
            url = new URI(uri.toString()).toURL();
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            if (connection.getResponseCode() == 200) {
                path = missingPaths.get(file);
                InputStream is = connection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is, 4096);
                FileOutputStream fos = new FileOutputStream(path);
                while ((readbytes = bis.read(buf, 0, 4096)) != -1) {
                    fos.write(buf, 0, readbytes);
                    Log.i(AnkiDroidApp.TAG, "Downloaded " + readbytes + " file: " + path);
                }
                fos.close();
                // Verify with checksum
                sum = missingSums.get(file);
                if (true) {
                    // sum.equals("") || sum.equals(Utils.fileChecksum(path))) {
                    grabbed++;
                } else {
                    // Download corrupted, delete file
                    Log.i(AnkiDroidApp.TAG, "Downloaded media file " + path + " failed checksum.");
                    File f = new File(path);
                    f.delete();
                    missing++;
                }
            } else {
                Log.e(AnkiDroidApp.TAG, "Connection error (" + connection.getResponseCode() + ") while retrieving media file " + urlbase + file);
                Log.e(AnkiDroidApp.TAG, "Connection message: " + connection.getResponseMessage());
                if (missingSums.get(file).equals("")) {
                    // Ignore and keep going
                    missing++;
                } else {
                    data.success = false;
                    data.data = new Object[] { file };
                    return data;
                }
            }
            connection.disconnect();
        } catch (URISyntaxException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
        } catch (MalformedURLException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
            Log.e(AnkiDroidApp.TAG, "MalformedURLException while download media file " + path);
            if (missingSums.get(file).equals("")) {
                // Ignore and keep going
                missing++;
            } else {
                data.success = false;
                data.data = new Object[] { file };
                return data;
            }
        } catch (IOException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
            Log.e(AnkiDroidApp.TAG, "IOException while download media file " + path);
            if (missingSums.get(file).equals("")) {
                // Ignore and keep going
                missing++;
            } else {
                data.success = false;
                data.data = new Object[] { file };
                return data;
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        publishProgress(Boolean.TRUE, new Integer(totalMissing), new Integer(grabbed + missing), syncName);
    }
    data.data[1] = new Integer(grabbed);
    data.data[2] = new Integer(missing);
    data.success = true;
    return data;
}
Also used : MalformedURLException(java.net.MalformedURLException) HashMap(java.util.HashMap) BufferedInputStream(java.io.BufferedInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) Decks(com.ichi2.libanki.Decks) Uri(android.net.Uri) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) Cursor(android.database.Cursor) URI(java.net.URI) URL(java.net.URL) HttpURLConnection(java.net.HttpURLConnection) BufferedInputStream(java.io.BufferedInputStream) FileOutputStream(java.io.FileOutputStream) JSONObject(org.json.JSONObject) File(java.io.File)

Example 4 with Decks

use of com.ichi2.libanki.Decks in project Anki-Android by Ramblurr.

the class MultimediaCardEditorActivity method showDeckSelectDialog.

private Dialog showDeckSelectDialog() {
    StyledDialog dialog = null;
    StyledDialog.Builder builder = new StyledDialog.Builder(this);
    ArrayList<CharSequence> dialogDeckItems = new ArrayList<CharSequence>();
    // Use this array to know which ID is associated with each
    // Item(name)
    final ArrayList<Long> dialogDeckIds = new ArrayList<Long>();
    ArrayList<JSONObject> decks = mCol.getDecks().all();
    Collections.sort(decks, new JSONNameComparator());
    builder.setTitle(R.string.deck);
    for (JSONObject d : decks) {
        try {
            if (d.getInt("dyn") == 0) {
                dialogDeckItems.add(d.getString("name"));
                dialogDeckIds.add(d.getLong("id"));
            }
        } catch (JSONException e) {
            Log.e("Multimedia Editor", e.getMessage());
        }
    }
    // Convert to Array
    String[] items = new String[dialogDeckItems.size()];
    dialogDeckItems.toArray(items);
    builder.setItems(items, new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int item) {
            long newId = dialogDeckIds.get(item);
            if (mCurrentDid != newId) {
                if (mAddNote) {
                    try {
                        // TODO: remove from dynamic deck (see CardEditor.java in v2.1)
                        // TODO: mEditorNote.setDid(newId);
                        mEditorNote.model().put("did", newId);
                        mCol.getModels().setChanged();
                    } catch (JSONException e) {
                        Log.e("Multimedia Editor", e.getMessage());
                    }
                }
                mCurrentDid = newId;
                createEditorUI(mNote);
            }
        }
    });
    dialog = builder.create();
    return dialog;
}
Also used : JSONNameComparator(com.ichi2.utils.JSONNameComparator) DialogInterface(android.content.DialogInterface) StyledDialog(com.ichi2.themes.StyledDialog) ArrayList(java.util.ArrayList) JSONException(org.json.JSONException) SuppressLint(android.annotation.SuppressLint) JSONObject(org.json.JSONObject)

Example 5 with Decks

use of com.ichi2.libanki.Decks in project Anki-Android by Ramblurr.

the class DownloadManagerService method addIncompleteDownloads.

// It could be part of the AIDL Interface but at the moment no Activity uses it directly
public void addIncompleteDownloads() {
    Log.i(AnkiDroidApp.TAG, "DownloadManagerService - Adding incomplete downloads:");
    File dir = new File(mDestination + "/tmp/");
    File[] fileList = dir.listFiles(new IncompleteDownloadsFilter());
    if (fileList != null) {
        for (File file : fileList) {
            String filename = file.getName();
            Log.i(AnkiDroidApp.TAG, "Filename = " + filename);
            // Personal decks
            if (filename.endsWith(".anki.tmp")) {
                Download download = new Download(filename.substring(0, filename.length() - ".anki.tmp".length()));
                download.setDownloaded(file.length());
                mPersonalDeckDownloads.add(download);
            } else // Shared decks
            if (filename.endsWith(".shared.zip.tmp")) {
                filename = filename.substring(0, filename.length() - ".shared.zip.tmp".length());
                int lastDotPosition = filename.lastIndexOf(".");
                String identifier = filename.substring(lastDotPosition + 1, filename.length());
                String title = filename.substring(0, lastDotPosition);
                SharedDeckDownload download = new SharedDeckDownload(Integer.parseInt(identifier), title);
                download.setDownloaded(file.length());
                mSharedDeckDownloads.add(download);
            } else // Shared but not totally updated decks
            if (filename.endsWith(".anki.updating")) {
                String title = filename.substring(0, filename.length() - ".anki.updating".length());
                SharedDeckDownload download = new SharedDeckDownload(title);
                SharedPreferences pref = AnkiDroidApp.getSharedPrefs(getBaseContext());
                String pausedPref = "paused:" + mDestination + "/tmp/" + download.getFilename() + ".anki.updating";
                if (pref.getBoolean(pausedPref, false)) {
                    download.setStatus(SharedDeckDownload.STATUS_PAUSED);
                } else {
                    download.setStatus(SharedDeckDownload.STATUS_UPDATING);
                }
                mSharedDeckDownloads.add(download);
            }
        }
        notifyObservers();
    }
    // If no decks were added, stop the service
    stopIfFinished();
}
Also used : SharedPreferences(android.content.SharedPreferences) RandomAccessFile(java.io.RandomAccessFile) File(java.io.File) SharedDeckDownload(com.ichi2.anki.SharedDeckDownload) Download(com.ichi2.anki.Download) SharedDeckDownload(com.ichi2.anki.SharedDeckDownload)

Aggregations

JSONObject (org.json.JSONObject)6 File (java.io.File)4 JSONException (org.json.JSONException)4 Collection (com.ichi2.libanki.Collection)3 DialogInterface (android.content.DialogInterface)2 SharedPreferences (android.content.SharedPreferences)2 Cursor (android.database.Cursor)2 SQLiteException (android.database.sqlite.SQLiteException)2 AnkiDb (com.ichi2.anki.AnkiDb)2 SharedDeckDownload (com.ichi2.anki.SharedDeckDownload)2 BasicHttpSyncer (com.ichi2.libanki.sync.BasicHttpSyncer)2 BufferedInputStream (java.io.BufferedInputStream)2 ByteArrayInputStream (java.io.ByteArrayInputStream)2 FileInputStream (java.io.FileInputStream)2 FileOutputStream (java.io.FileOutputStream)2 IOException (java.io.IOException)2 InputStream (java.io.InputStream)2 RandomAccessFile (java.io.RandomAccessFile)2 ArrayList (java.util.ArrayList)2 SuppressLint (android.annotation.SuppressLint)1