diff --git a/android/jni/com/mapswithme/maps/DownloadUI.cpp b/android/jni/com/mapswithme/maps/DownloadUI.cpp index c43c671ef5..a35ea76c86 100644 --- a/android/jni/com/mapswithme/maps/DownloadUI.cpp +++ b/android/jni/com/mapswithme/maps/DownloadUI.cpp @@ -1,16 +1,8 @@ -/* - * DownloadUI.cpp - * - * Created on: Oct 13, 2011 - * Author: siarheirachytski - */ - #include #include "Framework.hpp" #include "DownloadUI.hpp" #include "../jni/jni_thread.hpp" #include "../../../../../std/bind.hpp" -#include "../../../../../base/logging.hpp" android::DownloadUI * g_downloadUI = 0; @@ -25,7 +17,7 @@ namespace android m_onChangeCountry.reset(new jni::Method(k, "onChangeCountry", "(III)V")); m_onProgress.reset(new jni::Method(k, "onProgress", "(IIIJJ)V")); - ASSERT(!g_downloadUI, ()); + ASSERT(!g_downloadUI, ("DownloadUI is initialized twice")); g_downloadUI = this; } @@ -37,26 +29,12 @@ namespace android void DownloadUI::OnChangeCountry(storage::TIndex const & idx) { - jint group = idx.m_group; - jint country = idx.m_country; - jint region = idx.m_region; - - LOG(LINFO, ("Changed Country", group, country, region)); - - m_onChangeCountry->CallVoid(m_self, group, country, region); + m_onChangeCountry->CallVoid(m_self, idx.m_group, idx.m_country, idx.m_region); } void DownloadUI::OnProgress(storage::TIndex const & idx, pair const & p) { - jint group = idx.m_group; - jint country = idx.m_country; - jint region = idx.m_region; - jlong p1 = p.first; - jlong p2 = p.second; - - LOG(LINFO, ("Country Progress", group, country, region, p1, p2)); - - m_onProgress->CallVoid(m_self, group, country, region, p1, p2); + m_onProgress->CallVoid(m_self, idx.m_group, idx.m_country, idx.m_region, p.first, p.second); } } diff --git a/android/jni/com/mapswithme/maps/DownloadUI.hpp b/android/jni/com/mapswithme/maps/DownloadUI.hpp index 3a6aebabbe..2ba68ea730 100644 --- a/android/jni/com/mapswithme/maps/DownloadUI.hpp +++ b/android/jni/com/mapswithme/maps/DownloadUI.hpp @@ -1,8 +1,7 @@ #pragma once -#include "Framework.hpp" #include "../jni/jni_method.hpp" -#include "../../../../../base/base.hpp" +#include "../../../../../storage/storage.hpp" #include "../../../../../std/scoped_ptr.hpp" namespace android @@ -24,5 +23,3 @@ namespace android void OnProgress(storage::TIndex const & idx, pair const & p); }; } - -extern android::DownloadUI * g_downloadUI; diff --git a/android/jni/com/mapswithme/platform/HttpThread.cpp b/android/jni/com/mapswithme/platform/HttpThread.cpp index 8160ed06e7..8e7711134c 100644 --- a/android/jni/com/mapswithme/platform/HttpThread.cpp +++ b/android/jni/com/mapswithme/platform/HttpThread.cpp @@ -1,61 +1,49 @@ -#include "HttpThread.hpp" - #include "../../../../../platform/http_thread_callback.hpp" -#include "../../../../../std/string.hpp" -#include "../../../../../base/logging.hpp" - #include "../maps/DownloadUI.hpp" #include "../jni/jni_thread.hpp" -// @TODO empty stub, add android implementation - -HttpThread::HttpThread(string const & url, - downloader::IHttpThreadCallback & cb, - int64_t beg, - int64_t end, - int64_t size, - string const & pb) +class HttpThread { - LOG(LINFO, ("creating httpThread: ", &cb, url, beg, end, size, pb)); +private: + jobject m_self; - /// should create java object here. - JNIEnv * env = jni::GetCurrentThreadJNIEnv(); +public: + HttpThread(string const & url, + downloader::IHttpThreadCallback & cb, + int64_t beg, + int64_t end, + int64_t expectedFileSize, + string const & pb) + { + /// should create java object here. + JNIEnv * env = jni::GetCurrentThreadJNIEnv(); - jclass k = env->FindClass("com/mapswithme/maps/downloader/DownloadChunkTask"); + jclass k = env->FindClass("com/mapswithme/maps/downloader/DownloadChunkTask"); + ASSERT(k, ("Can't find java class com/mapswithme/maps/downloader/DownloadChunkTask")); - jni::Method ctor(k, "", "(JLjava/lang/String;JJJLjava/lang/String;)V"); + jni::Method ctor(k, "", "(JLjava/lang/String;JJJLjava/lang/String;)V"); - jlong _id = reinterpret_cast(&cb); - jlong _beg = static_cast(beg); - jlong _end = static_cast(end); - jlong _size = static_cast(size); - jstring _url = env->NewStringUTF(url.c_str()); - jstring _pb = env->NewStringUTF(pb.c_str()); + m_self = env->NewObject(k, ctor.GetMethodID(), reinterpret_cast(&cb), + env->NewStringUTF(url.c_str()), beg, end, expectedFileSize, env->NewStringUTF(pb.c_str())); - m_self = env->NewObject(k, ctor.GetMethodID(), _id, _url, _beg, _end, _size, _pb); + jni::Method startFn(k, "start", "()V"); - jni::Method startFn(k, "start", "()V"); + startFn.CallVoid(m_self); + } - startFn.CallVoid(m_self); -} + ~HttpThread() + { + jclass k = jni::GetCurrentThreadJNIEnv()->FindClass("com/mapswithme/maps/downloader/DownloadChunkTask"); + ASSERT(k, ("Can't find java class com/mapswithme/maps/downloader/DownloadChunkTask")); -HttpThread::~HttpThread() -{ - jclass k = jni::GetCurrentThreadJNIEnv()->FindClass("com/mapswithme/maps/downloader/DownloadChunkTask"); - - jni::Method markAsCancelledFn(k, "markAsCancelled", "()V"); - markAsCancelledFn.CallVoid(m_self); - - jni::Method cancelFn(k, "cancel", "(Z)Z"); - cancelFn.CallVoid(m_self, false); - - LOG(LINFO, ("destroying http_thread")); -} + jni::Method cancelFn(k, "cancel", "(Z)Z"); + cancelFn.CallVoid(m_self, false); + } +}; namespace downloader { - HttpThread * CreateNativeHttpThread(string const & url, downloader::IHttpThreadCallback & cb, int64_t beg, diff --git a/android/jni/com/mapswithme/platform/HttpThread.hpp b/android/jni/com/mapswithme/platform/HttpThread.hpp deleted file mode 100644 index d2a4db56e3..0000000000 --- a/android/jni/com/mapswithme/platform/HttpThread.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../../../../std/stdint.hpp" -#include "../../../../../std/string.hpp" -#include "../../../../../platform/http_thread_callback.hpp" -#include - -class HttpThread -{ -private: - - jobject m_self; - -public: - - HttpThread(string const & url, - downloader::IHttpThreadCallback & cb, - int64_t beg, - int64_t end, - int64_t size, - string const & pb); - ~HttpThread(); -}; diff --git a/android/src/com/mapswithme/maps/downloader/DownloadChunkTask.java b/android/src/com/mapswithme/maps/downloader/DownloadChunkTask.java index eb2d7604bd..2dd166cd48 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloadChunkTask.java +++ b/android/src/com/mapswithme/maps/downloader/DownloadChunkTask.java @@ -1,66 +1,48 @@ package com.mapswithme.maps.downloader; import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import org.apache.http.util.ByteArrayBuffer; - import android.os.AsyncTask; import android.util.Log; -// Checks if incomplete file exists and resumes it's download -// Downloads from scratch otherwise -class DownloadChunkTask extends AsyncTask +class DownloadChunkTask extends AsyncTask { - private final String TAG = "DownloadFilesTask"; + private static final String TAG = "DownloadChunkTask"; private long m_httpCallbackID; private String m_url; private long m_beg; private long m_end; + private long m_expectedFileSize; private String m_postBody; - private long m_requestedSize; - /// results of execution - - private List m_chunks; - private boolean m_hasError; - private boolean m_isCancelled; - private long m_response; - private long m_downloadedSize; - + private final int NOT_SET = -1; + private final int IO_ERROR = -2; + private final int INVALID_URL = -3; + private int m_httpErrorCode = NOT_SET; + private long m_downloadedBytes = 0; + native void onWrite(long httpCallbackID, long beg, byte [] data, long size); native void onFinish(long httpCallbackID, long httpCode, long beg, long end); - public DownloadChunkTask(long httpCallbackID, String url, long beg, long end, long size, String postBody) + public DownloadChunkTask(long httpCallbackID, String url, long beg, long end, long expectedFileSize, String postBody) { - Log.d(TAG, String.format("creating new task: %1$d, %2$s, %3$d, %4$d, %5$d, %6$s", httpCallbackID, url, beg, end, size, postBody)); - m_httpCallbackID = httpCallbackID; m_url = url; m_beg = beg; m_end = end; - m_requestedSize = end - beg + 1; + m_expectedFileSize = expectedFileSize; m_postBody = postBody; - m_chunks = new ArrayList(); - m_isCancelled = false; } - protected void markAsCancelled() - { - m_isCancelled = true; - } - @Override protected void onPreExecute() { @@ -69,46 +51,24 @@ class DownloadChunkTask extends AsyncTask @Override protected void onPostExecute(Void resCode) { - if (m_isCancelled) - { - Log.d(TAG, String.format("downloading was cancelled, chunk %1$d, %2$d", m_beg, m_end)); - return; - } - - if (!m_hasError) - { - byte [] buf = new byte[(int) m_downloadedSize]; - int offs = 0; - - Iterator it = m_chunks.iterator(); - - while (it.hasNext()) - { - byte [] chunk = it.next(); - System.arraycopy(chunk, 0, buf, offs, chunk.length); - offs += chunk.length; - } - - Log.d(TAG, "merged chunk " + m_downloadedSize + " byte length"); - - if (m_downloadedSize != m_requestedSize) - Log.d(TAG, "chunk size mismatch, requested " + m_requestedSize + " bytes, downloaded " + m_downloadedSize + " bytes"); - - onWrite(m_httpCallbackID, m_beg, buf, m_downloadedSize); - } - - Log.d(TAG, "finishing download with error code " + m_response); - onFinish(m_httpCallbackID, m_response, m_beg, m_end); + Log.d(TAG, "Successfully finishing download"); + onFinish(m_httpCallbackID, 200, m_beg, m_end); } @Override protected void onCancelled() { + // Report error in callback only if we're not forcibly canceled + if (m_httpErrorCode != NOT_SET) + onFinish(m_httpCallbackID, m_httpErrorCode, m_beg, m_end); } @Override - protected void onProgressUpdate(Long... progress) + protected void onProgressUpdate(byte []... data) { + // Use progress event to save downloaded bytes + onWrite(m_httpCallbackID, m_beg + m_downloadedBytes, data[0], (long)data[0].length); + m_downloadedBytes += data[0].length; } void start() @@ -134,29 +94,28 @@ class DownloadChunkTask extends AsyncTask return null; } - Log.d(TAG, String.format("configuring connection parameter for range %1$d : %2$d", m_beg, m_end)); + Log.d(TAG, String.format("configuring connection parameter for range %d : %d", m_beg, m_end)); - urlConnection.setDoOutput(true); urlConnection.setChunkedStreamingMode(0); - urlConnection.setRequestProperty("Content-Type", "application/json"); urlConnection.setUseCaches(false); urlConnection.setConnectTimeout(15 * 1000); + urlConnection.setReadTimeout(15 * 1000); - if (m_beg != -1) + // use Range header only if we don't download whole file from start + if (!(m_beg == 0 && m_end < 0)) { - Log.d(TAG, ("setting requested range")); - - if (m_end != -1) - urlConnection.setRequestProperty("Range", String.format("bytes=%1$d-%2$d", m_beg, m_end)); + if (m_end > 0) + urlConnection.setRequestProperty("Range", String.format("bytes=%d-%d", m_beg, m_end)); else - urlConnection.setRequestProperty("Range", String.format("bytes=%1$d-", m_beg)); + urlConnection.setRequestProperty("Range", String.format("bytes=%d-", m_beg)); } if (m_postBody.length() != 0) { Log.d(TAG, ("writing POST body")); - - DataOutputStream os = new DataOutputStream(urlConnection.getOutputStream()); + urlConnection.setDoOutput(true); + urlConnection.setRequestProperty("Content-Type", "application/json"); + final DataOutputStream os = new DataOutputStream(urlConnection.getOutputStream()); os.writeChars(m_postBody); } @@ -168,68 +127,46 @@ class DownloadChunkTask extends AsyncTask Log.d(TAG, ("getting response")); - int response = urlConnection.getResponseCode(); - if (response == HttpURLConnection.HTTP_OK - || response == HttpURLConnection.HTTP_PARTIAL) + final int err = urlConnection.getResponseCode(); + if (err != HttpURLConnection.HTTP_OK && err != HttpURLConnection.HTTP_PARTIAL) { - InputStream is = urlConnection.getInputStream(); - - byte [] tempBuf = new byte[1024 * 64]; - - try - { - while (true) - { - if (isCancelled()) - { - urlConnection.disconnect(); - return null; - } - - long readBytes = is.read(tempBuf); - - if (readBytes == -1) - { - Log.d(TAG, String.format("finished downloading interval %1$d : %2$d with response code %3$d", m_beg, m_end, response)); - m_hasError = false; - m_response = 200; - break; - } - else - { - byte [] chunk = new byte[(int)readBytes]; - System.arraycopy(tempBuf, 0, chunk, 0, (int)readBytes); - m_chunks.add(chunk); - m_downloadedSize += chunk.length; - } - } - } - catch (IOException ex) - { - Log.d(TAG, String.format("error occured while downloading range %1$d : %2$d, terminating chunk download", m_beg, m_end)); - - m_hasError = true; - m_response = -1; - } + // we've set error code so client should be notified about the error + m_httpErrorCode = err; + cancel(false); + urlConnection.disconnect(); + return null; } - else + + final InputStream is = urlConnection.getInputStream(); + + byte [] tempBuf = new byte[1024 * 64]; + long readBytes; + while ((readBytes = is.read(tempBuf)) != -1) { - m_hasError = true; - m_response = response; - Log.d(TAG, String.format("error downloading interval %1$d : %1$d , response code is %3$d", m_beg, m_end, response)); + if (isCancelled()) + { + urlConnection.disconnect(); + return null; + } + + byte [] chunk = new byte[(int)readBytes]; + System.arraycopy(tempBuf, 0, chunk, 0, (int)readBytes); + publishProgress(chunk); } } catch (MalformedURLException ex) { - Log.d(TAG, "invalid url : " + m_url); - m_hasError = true; - m_response = -1; + Log.d(TAG, "invalid url: " + m_url); + // Notify the client about error + m_httpErrorCode = INVALID_URL; + cancel(false); } catch (IOException ex) { - Log.d(TAG, "ioexception : " + ex.toString()); - m_hasError = true; - m_response = -1; + Log.d(TAG, "ioexception: " + ex.toString()); + // Notify the client about error + m_httpErrorCode = IO_ERROR; + cancel(false); } return null;