Search in sources :

Example 1 with Payload

use of com.ichi2.async.Connection.Payload 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 Payload

use of com.ichi2.async.Connection.Payload 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 3 with Payload

use of com.ichi2.async.Connection.Payload in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundLogin.

private Payload doInBackgroundLogin(Payload data) {
    String username = (String) data.data[0];
    String password = (String) data.data[1];
    BasicHttpSyncer server = new RemoteServer(this, null);
    HttpResponse ret = server.hostKey(username, password);
    String hostkey = null;
    boolean valid = false;
    if (ret != null) {
        data.returnType = ret.getStatusLine().getStatusCode();
        Log.i(AnkiDroidApp.TAG, "doInBackgroundLogin - response from server: " + data.returnType + " (" + ret.getStatusLine().getReasonPhrase() + ")");
        if (data.returnType == 200) {
            try {
                JSONObject jo = (new JSONObject(server.stream2String(ret.getEntity().getContent())));
                hostkey = jo.getString("key");
                valid = (hostkey != null) && (hostkey.length() > 0);
            } catch (JSONException e) {
                valid = false;
            } catch (IllegalStateException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    } else {
        Log.e(AnkiDroidApp.TAG, "doInBackgroundLogin - empty response from server");
    }
    if (valid) {
        data.success = true;
        data.data = new String[] { username, hostkey };
    } else {
        data.success = false;
    }
    return data;
}
Also used : BasicHttpSyncer(com.ichi2.libanki.sync.BasicHttpSyncer) JSONObject(org.json.JSONObject) HttpResponse(org.apache.http.HttpResponse) JSONException(org.json.JSONException) IOException(java.io.IOException) RemoteServer(com.ichi2.libanki.sync.RemoteServer)

Example 4 with Payload

use of com.ichi2.async.Connection.Payload in project Anki-Android by Ramblurr.

the class DownloadManagerService method resumeDownload.

// It could be part of the AIDL Interface but at the moment no Activity uses it directly
public void resumeDownload(Download download) {
    // Create tmp folder where the temporal decks are going to be stored
    new File(mDestination + "/tmp/").mkdirs();
    AnkiDroidApp.createNoMediaFileIfMissing(new File(mDestination));
    if (download instanceof SharedDeckDownload) {
        SharedDeckDownload sharedDeckDownload = (SharedDeckDownload) download;
        // numUpdatedCards and numTotalCards to get updated, so that progress is displayed correctly
        if (sharedDeckDownload.getStatus() == SharedDeckDownload.STATUS_PAUSED || sharedDeckDownload.getStatus() == SharedDeckDownload.STATUS_UPDATING) {
            new UpdateDeckTask().execute(new Payload(new Object[] { sharedDeckDownload }));
        } else {
            new DownloadSharedDeckTask().execute(sharedDeckDownload);
        }
    } else {
        // TODO: Check if there is already a deck with the same name, and if that's so
        // add the current milliseconds to the end of the name or notify the user
        new DownloadPersonalDeckTask().execute(download);
    }
}
Also used : Payload(com.ichi2.async.Connection.Payload) RandomAccessFile(java.io.RandomAccessFile) File(java.io.File) SharedDeckDownload(com.ichi2.anki.SharedDeckDownload)

Example 5 with Payload

use of com.ichi2.async.Connection.Payload in project Anki-Android by Ramblurr.

the class Feedback method postFeedback.

/**
     * Posting feedback or error info to the server. This is called from the AsyncTask.
     * 
     * @param url The url to post the feedback to.
     * @param type The type of the info, eg Feedback.TYPE_CRASH_STACKTRACE.
     * @param feedback For feedback types this is the message. For error/crash types this is the path to the error file.
     * @param groupId A single time generated ID, so that errors/feedback send together can be grouped together.
     * @param index The index of the error in the list
     * @return A Payload file showing success, response code and response message.
     */
public static Payload postFeedback(String url, String type, String feedback, String groupId, int index, Application app) {
    Payload result = new Payload(null);
    List<NameValuePair> pairs = null;
    if (!isErrorType(type)) {
        pairs = new ArrayList<NameValuePair>();
        pairs.add(new BasicNameValuePair("type", type));
        pairs.add(new BasicNameValuePair("groupid", groupId));
        pairs.add(new BasicNameValuePair("index", "0"));
        pairs.add(new BasicNameValuePair("message", feedback));
        addTimestamp(pairs);
    } else {
        pairs = Feedback.extractPairsFromError(type, feedback, groupId, index, app);
        if (pairs == null) {
            result.success = false;
            result.result = null;
        }
    }
    HttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(url);
    httpPost.addHeader("User-Agent", "AnkiDroid");
    try {
        httpPost.setEntity(new UrlEncodedFormEntity(pairs));
        HttpResponse response = httpClient.execute(httpPost);
        Log.e(AnkiDroidApp.TAG, String.format("Bug report posted to %s", url));
        int respCode = response.getStatusLine().getStatusCode();
        switch(respCode) {
            case 200:
                result.success = true;
                result.returnType = respCode;
                result.result = Utils.convertStreamToString(response.getEntity().getContent());
                Log.i(AnkiDroidApp.TAG, String.format("postFeedback OK: %s", result.result));
                break;
            default:
                Log.e(AnkiDroidApp.TAG, String.format("postFeedback failure: %d - %s", response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()));
                result.success = false;
                result.returnType = respCode;
                result.result = response.getStatusLine().getReasonPhrase();
                break;
        }
    } catch (ClientProtocolException ex) {
        Log.e(AnkiDroidApp.TAG, "ClientProtocolException: " + ex.toString());
        result.success = false;
        result.result = ex.toString();
    } catch (IOException ex) {
        Log.e(AnkiDroidApp.TAG, "IOException: " + ex.toString());
        result.success = false;
        result.result = ex.toString();
    }
    return result;
}
Also used : BasicNameValuePair(org.apache.http.message.BasicNameValuePair) NameValuePair(org.apache.http.NameValuePair) HttpPost(org.apache.http.client.methods.HttpPost) HttpResponse(org.apache.http.HttpResponse) UrlEncodedFormEntity(org.apache.http.client.entity.UrlEncodedFormEntity) IOException(java.io.IOException) DefaultHttpClient(org.apache.http.impl.client.DefaultHttpClient) ClientProtocolException(org.apache.http.client.ClientProtocolException) BasicNameValuePair(org.apache.http.message.BasicNameValuePair) DefaultHttpClient(org.apache.http.impl.client.DefaultHttpClient) HttpClient(org.apache.http.client.HttpClient) Payload(com.ichi2.async.Connection.Payload)

Aggregations

IOException (java.io.IOException)6 JSONObject (org.json.JSONObject)5 BasicHttpSyncer (com.ichi2.libanki.sync.BasicHttpSyncer)4 HttpResponse (org.apache.http.HttpResponse)4 Payload (com.ichi2.async.Connection.Payload)3 RemoteServer (com.ichi2.libanki.sync.RemoteServer)3 File (java.io.File)3 JSONException (org.json.JSONException)3 Collection (com.ichi2.libanki.Collection)2 BufferedInputStream (java.io.BufferedInputStream)2 ByteArrayInputStream (java.io.ByteArrayInputStream)2 FileInputStream (java.io.FileInputStream)2 FileNotFoundException (java.io.FileNotFoundException)2 FileOutputStream (java.io.FileOutputStream)2 InputStream (java.io.InputStream)2 ClientProtocolException (org.apache.http.client.ClientProtocolException)2 Context (android.content.Context)1 Intent (android.content.Intent)1 SharedPreferences (android.content.SharedPreferences)1 Resources (android.content.res.Resources)1