From ffd39d13783c48f6dc5688006861a04d643db627 Mon Sep 17 00:00:00 2001 From: vng Date: Sat, 24 Apr 2021 20:19:22 +0300 Subject: [PATCH] Merge remote-tracking branch 'mapsme/master' into vng-merge Signed-off-by: vng --- android/AndroidManifest.xml | 15 ++ android/jni/CMakeLists.txt | 2 + .../com/mapswithme/maps/DownloaderAdapter.cpp | 195 ++++++++++++++++++ .../com/mapswithme/maps/DownloaderAdapter.hpp | 48 +++++ .../jni/com/mapswithme/maps/MapManager.cpp | 12 ++ android/multidex-config.txt | 1 + .../com/mapswithme/maps/MwmApplication.java | 18 +- .../AbstractLogBroadcastReceiver.java | 55 +++++ .../SystemDownloadCompletedReceiver.java | 30 +++ .../SystemDownloadCompletedService.java | 67 ++++++ .../maps/downloader/DownloaderFragment.java | 16 ++ .../MapDownloadCompletedProcessor.java | 122 +++++++++++ .../maps/downloader/MapDownloadManager.java | 167 +++++++++++++++ .../MapDownloadProgressTracker.java | 106 ++++++++++ .../maps/downloader/MapManager.java | 7 + .../maps/downloader/OnmapDownloader.java | 6 + .../mapswithme/maps/scheduling/JobIdMap.java | 2 + .../maps/widget/placepage/PlacePageView.java | 3 + generator/transit_generator_experimental.cpp | 12 +- platform/downloader_utils.cpp | 37 +++- platform/downloader_utils.hpp | 2 + .../platform_tests/downloader_utils_tests.cpp | 49 ++++- routing/index_router.cpp | 9 +- routing/index_router.hpp | 5 +- storage/CMakeLists.txt | 12 +- .../downloader_adapter_ios.h | 8 +- .../downloader_adapter_ios.mm | 16 +- .../downloader_queue.hpp | 103 +++++++++ .../downloader_queue_ios.cpp | 56 ----- .../downloader_queue_ios.hpp | 47 ----- ...eue.hpp => downloader_queue_interface.hpp} | 1 + storage/downloader_queue_universal.cpp | 10 +- storage/downloader_queue_universal.hpp | 5 +- storage/pinger.cpp | 41 ++-- transit/transit_schedule.cpp | 6 +- transit/world_feed/world_feed.cpp | 15 +- .../storage/storage.xcodeproj/project.pbxproj | 20 +- 37 files changed, 1146 insertions(+), 180 deletions(-) create mode 100644 android/jni/com/mapswithme/maps/DownloaderAdapter.cpp create mode 100644 android/jni/com/mapswithme/maps/DownloaderAdapter.hpp create mode 100644 android/src/com/mapswithme/maps/background/AbstractLogBroadcastReceiver.java create mode 100644 android/src/com/mapswithme/maps/background/SystemDownloadCompletedReceiver.java create mode 100644 android/src/com/mapswithme/maps/background/SystemDownloadCompletedService.java create mode 100644 android/src/com/mapswithme/maps/downloader/MapDownloadCompletedProcessor.java create mode 100644 android/src/com/mapswithme/maps/downloader/MapDownloadManager.java create mode 100644 android/src/com/mapswithme/maps/downloader/MapDownloadProgressTracker.java create mode 100644 storage/background_downloading/downloader_queue.hpp delete mode 100644 storage/background_downloading/downloader_queue_ios.cpp delete mode 100644 storage/background_downloading/downloader_queue_ios.hpp rename storage/{downloader_queue.hpp => downloader_queue_interface.hpp} (92%) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 3122eb73e5..49e3abdc05 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -25,6 +25,7 @@ + diff --git a/android/jni/CMakeLists.txt b/android/jni/CMakeLists.txt index bf28668a9f..66fbb859f2 100644 --- a/android/jni/CMakeLists.txt +++ b/android/jni/CMakeLists.txt @@ -26,6 +26,7 @@ set( com/mapswithme/core/logging.hpp com/mapswithme/core/ScopedEnv.hpp com/mapswithme/core/ScopedLocalRef.hpp + com/mapswithme/maps/DownloaderAdapter.hpp com/mapswithme/maps/Framework.hpp com/mapswithme/maps/SearchEngine.hpp com/mapswithme/opengl/android_gl_utils.hpp @@ -42,6 +43,7 @@ set( com/mapswithme/core/logging.cpp com/mapswithme/maps/bookmarks/data/BookmarkManager.cpp com/mapswithme/maps/DisplayedCategories.cpp + com/mapswithme/maps/DownloaderAdapter.cpp com/mapswithme/maps/DownloadResourcesLegacyActivity.cpp com/mapswithme/maps/editor/Editor.cpp com/mapswithme/maps/editor/OpeningHours.cpp diff --git a/android/jni/com/mapswithme/maps/DownloaderAdapter.cpp b/android/jni/com/mapswithme/maps/DownloaderAdapter.cpp new file mode 100644 index 0000000000..38591950f4 --- /dev/null +++ b/android/jni/com/mapswithme/maps/DownloaderAdapter.cpp @@ -0,0 +1,195 @@ +#include "com/mapswithme/maps/DownloaderAdapter.hpp" + +#include "com/mapswithme/core/jni_helper.hpp" +#include "com/mapswithme/platform/Platform.hpp" + +#include "storage/downloader.hpp" + +#include "base/assert.hpp" +#include "base/file_name_utils.hpp" +#include "base/string_utils.hpp" + +#include +#include +#include + +using Callbacks = std::pair, + std::function>; +static std::unordered_map g_completionHandlers; + +namespace storage +{ +BackgroundDownloaderAdapter::BackgroundDownloaderAdapter() +{ + auto env = jni::GetEnv(); + auto downloadManagerClazz = jni::GetGlobalClassRef(env, "com/mapswithme/maps/downloader/MapDownloadManager"); + auto from = jni::GetStaticMethodID(env, downloadManagerClazz, "from", + "(Landroid/content/Context;)Lcom/mapswithme/maps/downloader/MapDownloadManager;"); + + auto context = android::Platform::Instance().GetContext(); + m_downloadManager = jni::make_global_ref(env->CallStaticObjectMethod(downloadManagerClazz, from, context)); + m_downloadManagerRemove = env->GetMethodID(downloadManagerClazz, "remove","(J)V"); + m_downloadManagerEnqueue = env->GetMethodID(downloadManagerClazz, "enqueue", "(Ljava/lang/String;)J"); + jni::HandleJavaException(env); +} + +BackgroundDownloaderAdapter::~BackgroundDownloaderAdapter() +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + g_completionHandlers.clear(); +} + +void BackgroundDownloaderAdapter::Remove(CountryId const & countryId) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + MapFilesDownloader::Remove(countryId); + + if (!m_queue.Contains(countryId)) + return; + + auto const id = m_queue.GetTaskInfoForCountryId(countryId); + if (id) + { + RemoveByRequestId(*id); + g_completionHandlers.erase(*id); + } + m_queue.Remove(countryId); + +} + +void BackgroundDownloaderAdapter::Clear() +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + MapFilesDownloader::Clear(); + + m_queue.ForEachTaskInfo([this](auto const id) + { + RemoveByRequestId(id); + g_completionHandlers.erase(id); + }); + + m_queue.Clear(); +}; + +QueueInterface const & BackgroundDownloaderAdapter::GetQueue() const +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + if (m_queue.IsEmpty()) + return MapFilesDownloader::GetQueue(); + + return m_queue; +} + +void BackgroundDownloaderAdapter::Download(QueuedCountry && queuedCountry) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + if (!IsDownloadingAllowed()) + { + queuedCountry.OnDownloadFinished(downloader::DownloadStatus::Failed); + return; + } + + auto const countryId = queuedCountry.GetCountryId(); + auto urls = MakeUrlList(queuedCountry.GetRelativeUrl()); + auto const path = queuedCountry.GetFileDownloadPath(); + + m_queue.Append(std::move(queuedCountry)); + + DownloadFromLastUrl(countryId, path, std::move(urls)); +} + +void BackgroundDownloaderAdapter::DownloadFromLastUrl(CountryId const & countryId, + std::string const & downloadPath, + std::vector && urls) +{ + CHECK_THREAD_CHECKER(m_threadChecker, ()); + ASSERT(!urls.empty(), ()); + + auto env = jni::GetEnv(); + jni::TScopedLocalRef url(env, jni::ToJavaString(env, urls.back())); + auto id = static_cast(env->CallLongMethod(*m_downloadManager, m_downloadManagerEnqueue, url.get())); + + jni::HandleJavaException(env); + + m_queue.SetTaskInfoForCountryId(countryId, id); + + urls.pop_back(); + auto onFinish = [this, countryId, downloadPath, urls = std::move(urls)](bool status) mutable + { + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + if (!m_queue.Contains(countryId)) + return; + + if (!status && urls.size() > 1) + { + urls.pop_back(); + DownloadFromLastUrl(countryId, downloadPath, std::move(urls)); + } + else + { + auto const country = m_queue.GetCountryById(countryId); + m_queue.Remove(countryId); + country.OnDownloadFinished(status + ? downloader::DownloadStatus::Completed + : downloader::DownloadStatus::Failed); + } + }; + + auto onProgress = [this, countryId](int64_t bytesDownloaded, int64_t bytesTotal) + { + CHECK_THREAD_CHECKER(m_threadChecker, ()); + + if (!m_queue.Contains(countryId)) + return; + + auto const & country = m_queue.GetCountryById(countryId); + country.OnDownloadProgress({bytesDownloaded, bytesTotal}); + }; + + g_completionHandlers.emplace(id, Callbacks(onFinish, onProgress)); +} + +void BackgroundDownloaderAdapter::RemoveByRequestId(int64_t id) +{ + auto env = jni::GetEnv(); + env->CallVoidMethod(*m_downloadManager, m_downloadManagerRemove, static_cast(id)); + + jni::HandleJavaException(env); +} + +std::unique_ptr GetDownloader() +{ + return std::make_unique(); +} +} // namespace storage + +extern "C" { +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_downloader_MapManager_nativeOnDownloadFinished(JNIEnv *, jclass, + jboolean status, jlong id) +{ + auto const it = g_completionHandlers.find(static_cast(id)); + if (it == g_completionHandlers.end()) + return; + + it->second.first(static_cast(status)); +} + +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_downloader_MapManager_nativeOnDownloadProgress(JNIEnv *, jclass, jlong id, + jlong bytesDownloaded, + jlong bytesTotal) +{ + auto const it = g_completionHandlers.find(static_cast(id)); + if (it == g_completionHandlers.end()) + return; + + it->second.second(static_cast(bytesDownloaded), static_cast(bytesTotal)); +} +} // extern "C" diff --git a/android/jni/com/mapswithme/maps/DownloaderAdapter.hpp b/android/jni/com/mapswithme/maps/DownloaderAdapter.hpp new file mode 100644 index 0000000000..b7772546ab --- /dev/null +++ b/android/jni/com/mapswithme/maps/DownloaderAdapter.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "storage/background_downloading/downloader_queue.hpp" +#include "storage/storage_defines.hpp" +#include "storage/map_files_downloader_with_ping.hpp" + +#include "base/thread_checker.hpp" + +#include + +#include +#include +#include + +namespace storage +{ +class BackgroundDownloaderAdapter : public MapFilesDownloaderWithPing +{ +public: + BackgroundDownloaderAdapter(); + ~BackgroundDownloaderAdapter(); + + // MapFilesDownloader overrides: + void Remove(CountryId const & countryId) override; + + void Clear() override; + + QueueInterface const & GetQueue() const override; + +private: + // MapFilesDownloaderWithServerList overrides: + void Download(QueuedCountry && queuedCountry) override; + + // Trying to download mwm from different servers recursively. + void DownloadFromLastUrl(CountryId const & countryId, std::string const & downloadPath, + std::vector && urls); + + void RemoveByRequestId(int64_t id); + + BackgroundDownloaderQueue m_queue; + + jmethodID m_downloadManagerRemove = nullptr; + jmethodID m_downloadManagerEnqueue = nullptr; + std::shared_ptr m_downloadManager; + + DECLARE_THREAD_CHECKER(m_threadChecker); +}; +} // namespace storage diff --git a/android/jni/com/mapswithme/maps/MapManager.cpp b/android/jni/com/mapswithme/maps/MapManager.cpp index 9eefd7333a..5b470b3c19 100644 --- a/android/jni/com/mapswithme/maps/MapManager.cpp +++ b/android/jni/com/mapswithme/maps/MapManager.cpp @@ -11,6 +11,7 @@ #include "base/thread_checker.hpp" #include "platform/downloader_defines.hpp" +#include "platform/downloader_utils.hpp" #include "platform/local_country_file_utils.hpp" #include "platform/mwm_version.hpp" @@ -576,4 +577,15 @@ Java_com_mapswithme_maps_downloader_MapManager_nativeGetSelectedCountry(JNIEnv * return (res == storage::kInvalidCountryId ? nullptr : jni::ToJavaString(env, res)); } +JNIEXPORT jboolean JNICALL +Java_com_mapswithme_maps_downloader_MapManager_nativeIsUrlSupported(JNIEnv * env, jclass, jstring url) +{ + return static_cast(downloader::IsUrlSupported(jni::ToNativeString(env, url))); +} + +JNIEXPORT jstring JNICALL +Java_com_mapswithme_maps_downloader_MapManager_nativeGetFilePathByUrl(JNIEnv * env, jclass, jstring url) +{ + return jni::ToJavaString(env, downloader::GetFilePathByUrl(jni::ToNativeString(env, url))); +} } // extern "C" diff --git a/android/multidex-config.txt b/android/multidex-config.txt index 5a82efb92e..79b7a1b2d7 100644 --- a/android/multidex-config.txt +++ b/android/multidex-config.txt @@ -4,6 +4,7 @@ com/mapswithme/maps/bookmarks/data/Bookmark.class com/mapswithme/maps/bookmarks/data/ElevationInfo.class com/mapswithme/maps/bookmarks/data/FeatureId.class com/mapswithme/maps/bookmarks/data/MapObject.class +com/mapswithme/maps/downloader/MapDownloadManager.class com/mapswithme/maps/location/PlatformSocket.class com/mapswithme/maps/search/PopularityProvider.class com/mapswithme/maps/widget/placepage/PlacePageData.class diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index 42fbc603fb..bc5427fce2 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -17,6 +17,7 @@ import com.mapswithme.maps.background.Notifier; import com.mapswithme.maps.base.MediaPlayerWrapper; import com.mapswithme.maps.bookmarks.data.BookmarkManager; import com.mapswithme.maps.downloader.CountryItem; +import com.mapswithme.maps.downloader.MapDownloadManager; import com.mapswithme.maps.downloader.MapManager; import com.mapswithme.maps.editor.Editor; import com.mapswithme.maps.location.LocationHelper; @@ -45,20 +46,24 @@ import java.util.List; public class MwmApplication extends Application implements AppBackgroundTracker.OnTransitionListener { - @SuppressWarnings("NullableProblems") + @SuppressWarnings("NotNullFieldNotInitialized") @NonNull private Logger mLogger; public final static String TAG = "MwmApplication"; private AppBackgroundTracker mBackgroundTracker; - @SuppressWarnings("NullableProblems") + @SuppressWarnings("NotNullFieldNotInitialized") @NonNull private SubwayManager mSubwayManager; - @SuppressWarnings("NullableProblems") + @SuppressWarnings("NotNullFieldNotInitialized") @NonNull private IsolinesManager mIsolinesManager; + @SuppressWarnings("NotNullFieldNotInitialized") + @NonNull + private MapDownloadManager mMapDownloadManager; + private boolean mFrameworkInitialized; private boolean mPlatformInitialized; @@ -144,6 +149,7 @@ public class MwmApplication extends Application implements AppBackgroundTracker. mBackgroundTracker = new AppBackgroundTracker(this); mSubwayManager = new SubwayManager(this); mIsolinesManager = new IsolinesManager(this); + mMapDownloadManager = new MapDownloadManager(this); mPlayer = new MediaPlayerWrapper(this); WebView.setWebContentsDebuggingEnabled(Utils.isDebugOrBeta()); @@ -320,6 +326,12 @@ public class MwmApplication extends Application implements AppBackgroundTracker. nativeOnTransit(foreground); } + @NonNull + public MapDownloadManager getMapDownloadManager() + { + return mMapDownloadManager; + } + private class StorageCallbackImpl implements MapManager.StorageCallback { @Override diff --git a/android/src/com/mapswithme/maps/background/AbstractLogBroadcastReceiver.java b/android/src/com/mapswithme/maps/background/AbstractLogBroadcastReceiver.java new file mode 100644 index 0000000000..06ac0b7c2a --- /dev/null +++ b/android/src/com/mapswithme/maps/background/AbstractLogBroadcastReceiver.java @@ -0,0 +1,55 @@ +package com.mapswithme.maps.background; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import androidx.annotation.NonNull; + +import android.text.TextUtils; +import android.util.Log; + +import com.mapswithme.util.CrashlyticsUtils; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; + +import static com.mapswithme.maps.MwmApplication.backgroundTracker; + +public abstract class AbstractLogBroadcastReceiver extends BroadcastReceiver +{ + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC); + + @Override + public final void onReceive(Context context, Intent intent) + { + if (intent == null) + { + LOGGER.w(getTag(), "A null intent detected"); + return; + } + + String action = intent.getAction(); + if (!TextUtils.equals(getAssertAction(), action)) + { + LOGGER.w(getTag(), "An intent with wrong action detected: " + action); + return; + } + + String msg = "onReceive: " + intent + " app in background = " + + !backgroundTracker(context).isForeground(); + LOGGER.i(getTag(), msg); + CrashlyticsUtils.INSTANCE.log(Log.INFO, getTag(), msg); + onReceiveInternal(context, intent); + } + + @NonNull + protected String getTag() + { + return getClass().getSimpleName(); + } + + @NonNull + protected abstract String getAssertAction(); + + @SuppressWarnings("unused") + public abstract void onReceiveInternal(@NonNull Context context, @NonNull Intent intent); +} diff --git a/android/src/com/mapswithme/maps/background/SystemDownloadCompletedReceiver.java b/android/src/com/mapswithme/maps/background/SystemDownloadCompletedReceiver.java new file mode 100644 index 0000000000..3d55e6b06e --- /dev/null +++ b/android/src/com/mapswithme/maps/background/SystemDownloadCompletedReceiver.java @@ -0,0 +1,30 @@ +package com.mapswithme.maps.background; + +import android.app.DownloadManager; +import android.content.Context; +import android.content.Intent; +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; + +import com.mapswithme.maps.scheduling.JobIdMap; + +public class SystemDownloadCompletedReceiver extends AbstractLogBroadcastReceiver +{ + @NonNull + @Override + protected String getAssertAction() + { + return DownloadManager.ACTION_DOWNLOAD_COMPLETE; + } + + @Override + public void onReceiveInternal(@NonNull Context context, @NonNull Intent intent) + { + DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + if (manager == null) + return; + intent.setClass(context, SystemDownloadCompletedService.class); + int jobId = JobIdMap.getId(SystemDownloadCompletedService.class); + JobIntentService.enqueueWork(context, SystemDownloadCompletedService.class, jobId, intent); + } +} diff --git a/android/src/com/mapswithme/maps/background/SystemDownloadCompletedService.java b/android/src/com/mapswithme/maps/background/SystemDownloadCompletedService.java new file mode 100644 index 0000000000..5330c1c808 --- /dev/null +++ b/android/src/com/mapswithme/maps/background/SystemDownloadCompletedService.java @@ -0,0 +1,67 @@ +package com.mapswithme.maps.background; + +import android.app.DownloadManager; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; + +import androidx.annotation.NonNull; +import androidx.core.app.JobIntentService; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.maps.downloader.MapDownloadCompletedProcessor; +import com.mapswithme.util.Utils; + +public class SystemDownloadCompletedService extends JobIntentService +{ + private interface DownloadProcessor + { + boolean process(@NonNull Context context, long id, @NonNull Cursor cursor); + } + + @Override + public void onCreate() + { + super.onCreate(); + MwmApplication app = (MwmApplication) getApplication(); + if (app.arePlatformAndCoreInitialized()) + return; + app.initCore(); + } + + @Override + protected void onHandleWork(@NonNull Intent intent) + { + DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); + if (manager == null) + throw new IllegalStateException("Failed to get a download manager"); + + processIntent(manager, intent); + } + + private void processIntent(@NonNull DownloadManager manager, @NonNull Intent intent) + { + Cursor cursor = null; + try + { + final long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); + DownloadManager.Query query = new DownloadManager.Query().setFilterById(id); + cursor = manager.query(query); + if (!cursor.moveToFirst()) + return; + + final DownloadProcessor[] processors = { + MapDownloadCompletedProcessor::process + }; + + for (DownloadProcessor processor : processors) + { + if (processor.process(getApplicationContext(), id, cursor)) + break; + } + } + finally + { + Utils.closeSafely(cursor); + } + } +} diff --git a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java index c37634d448..a2fb90b728 100644 --- a/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java +++ b/android/src/com/mapswithme/maps/downloader/DownloaderFragment.java @@ -171,6 +171,22 @@ public class DownloaderFragment extends BaseMwmRecyclerFragment 0) + out.write(buf, 0, len); + + context.getContentResolver().delete(from, null, null); + return true; + } + } + catch (IOException e) + { + LOGGER.e(TAG, "Failed to copy or delete downloaded map file from " + from.toString() + + " to " + to + ". Exception ", e); + return false; + } + } + + private static class MapDownloadCompletedTask implements Runnable + { + @NonNull + private final Context mAppContext; + private final boolean mStatus; + private final long mId; + + private MapDownloadCompletedTask(@NonNull Context applicationContext, boolean status, long id) + { + mAppContext = applicationContext; + mStatus = status; + mId = id; + } + + @Override + @MainThread + public void run() + { + MwmApplication application = MwmApplication.from(mAppContext); + MapDownloadManager manager = MapDownloadManager.from(application); + manager.onDownloadFinished(mStatus, mId); + } + } +} diff --git a/android/src/com/mapswithme/maps/downloader/MapDownloadManager.java b/android/src/com/mapswithme/maps/downloader/MapDownloadManager.java new file mode 100644 index 0000000000..0539f3b408 --- /dev/null +++ b/android/src/com/mapswithme/maps/downloader/MapDownloadManager.java @@ -0,0 +1,167 @@ +package com.mapswithme.maps.downloader; + +import android.app.DownloadManager; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.mapswithme.maps.MwmApplication; +import com.mapswithme.util.Utils; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; + +public class MapDownloadManager +{ + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.DOWNLOADER); + private static final String TAG = MapDownloadManager.class.getSimpleName(); + + @NonNull + private DownloadManager mDownloadManager; + @Nullable + private Map mRestoredRequests; + @NonNull + private MapDownloadProgressTracker mProgressTracker; + + public MapDownloadManager(@NonNull Context context) + { + DownloadManager downloadManager = + (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + + if (downloadManager == null) + throw new NullPointerException("Download manager is null, failed to create MapDownloadManager"); + + mDownloadManager = downloadManager; + mProgressTracker = new MapDownloadProgressTracker(context); + } + + @NonNull + private Map loadEnqueued() + { + Map result = new HashMap<>(); + DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_RUNNING | + DownloadManager.STATUS_PAUSED | DownloadManager.STATUS_SUCCESSFUL); + Cursor cursor = null; + try + { + cursor = mDownloadManager.query(query); + + cursor.moveToFirst(); + int count = cursor.getCount(); + + for (int i = 0; i < count; ++i) + { + long id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)); + String url = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)); + String urlPath = getUrlPath(url); + + if (!TextUtils.isEmpty(urlPath)) + result.put(urlPath, id); + + cursor.moveToNext(); + } + } + catch (Exception e) + { + LOGGER.e(TAG, "Failed to load enqueued requests. Exception ", e); + } + finally + { + Utils.closeSafely(cursor); + } + + return result; + } + + @Nullable + private String getUrlPath(@Nullable String url) + { + if (TextUtils.isEmpty(url)) + return null; + + String path = Uri.parse(url).getPath(); + if (TextUtils.isEmpty(path)|| !MapManager.nativeIsUrlSupported(path)) + return null; + + try + { + return URLDecoder.decode(path, "UTF-8"); + } + catch (UnsupportedEncodingException ignored) + { + return null; + } + } + + @MainThread + public long enqueue(@NonNull String url) + { + Uri uri = Uri.parse(url); + String uriPath = uri.getPath(); + if (uriPath == null) + throw new AssertionError("The path must be not null"); + + if (mRestoredRequests == null) + mRestoredRequests = loadEnqueued(); + + Long id = mRestoredRequests.get(uriPath); + long requestId = 0; + + if (id == null) + { + DownloadManager.Request request = new DownloadManager + .Request(uri) + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); + + requestId = mDownloadManager.enqueue(request); + } + else + { + mRestoredRequests.remove(uriPath); + requestId = id; + } + + mProgressTracker.add(requestId); + + return requestId; + } + + @MainThread + public void remove(long requestId) + { + mProgressTracker.remove(requestId); + mDownloadManager.remove(requestId); + } + + public void onDownloadFinished(boolean status, long id) + { + mProgressTracker.remove(id); + MapManager.nativeOnDownloadFinished(status, id); + } + + public void startProgressTracking() + { + mProgressTracker.start(); + } + + public void stopProgressTracking() + { + mProgressTracker.stop(); + } + + @NonNull + public static MapDownloadManager from(@NonNull Context context) + { + MwmApplication app = (MwmApplication) context.getApplicationContext(); + return app.getMapDownloadManager(); + } +} diff --git a/android/src/com/mapswithme/maps/downloader/MapDownloadProgressTracker.java b/android/src/com/mapswithme/maps/downloader/MapDownloadProgressTracker.java new file mode 100644 index 0000000000..e78e24638c --- /dev/null +++ b/android/src/com/mapswithme/maps/downloader/MapDownloadProgressTracker.java @@ -0,0 +1,106 @@ +package com.mapswithme.maps.downloader; + +import android.app.DownloadManager; +import android.content.Context; +import android.database.Cursor; + +import androidx.annotation.NonNull; +import com.mapswithme.util.Utils; +import com.mapswithme.util.concurrency.UiThread; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; + +import java.util.HashSet; +import java.util.Set; + +public class MapDownloadProgressTracker +{ + private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.DOWNLOADER); + private static final String TAG = MapDownloadProgressTracker.class.getSimpleName(); + + private static final long PROGRESS_TRACKING_INTERVAL_MILLISECONDS = 1000; + + @NonNull + private final DownloadManager mDownloadManager; + @NonNull + private final Set mTrackingIds = new HashSet<>(); + private boolean mTrackingEnabled = false; + private final Runnable mTrackingMethod = this::trackProgress; + + MapDownloadProgressTracker(@NonNull Context context) + { + DownloadManager downloadManager = + (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + + if (downloadManager == null) + throw new NullPointerException("Download manager is null, failed to create MapDownloadManager"); + + mDownloadManager = downloadManager; + } + + public void start() + { + if (mTrackingEnabled) + return; + + mTrackingEnabled = true; + trackProgress(); + } + + public void stop() + { + mTrackingEnabled = false; + UiThread.cancelDelayedTasks(mTrackingMethod); + } + + public void add(long id) + { + mTrackingIds.add(id); + } + + public void remove(long id) + { + mTrackingIds.remove(id); + } + + private void trackProgress() + { + if (!mTrackingEnabled) + return; + + DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterByStatus(DownloadManager.STATUS_RUNNING); + Cursor cursor = null; + try + { + cursor = mDownloadManager.query(query); + + cursor.moveToFirst(); + int count = cursor.getCount(); + for (int i = 0; i < count; ++i) + { + long id = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_ID)); + if (!mTrackingIds.contains(id)) + continue; + + long bytesDownloaded = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + long bytesTotal = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + + MapManager.nativeOnDownloadProgress(id, bytesDownloaded, bytesTotal); + + cursor.moveToNext(); + } + + UiThread.runLater(mTrackingMethod, PROGRESS_TRACKING_INTERVAL_MILLISECONDS); + } + catch (Exception e) + { + LOGGER.e(TAG, "Downloading progress tracking failed. Exception: " + e); + stop(); + } + finally + { + Utils.closeSafely(cursor); + } + } +} diff --git a/android/src/com/mapswithme/maps/downloader/MapManager.java b/android/src/com/mapswithme/maps/downloader/MapManager.java index 5b300b6fae..e8acc8d509 100644 --- a/android/src/com/mapswithme/maps/downloader/MapManager.java +++ b/android/src/com/mapswithme/maps/downloader/MapManager.java @@ -411,4 +411,11 @@ public final class MapManager * Returns country ID which the current PP object points to, or {@code null}. */ public static native @Nullable String nativeGetSelectedCountry(); + + public static native boolean nativeIsUrlSupported(@NonNull String url); + @NonNull + public static native String nativeGetFilePathByUrl(@NonNull String url); + + public static native void nativeOnDownloadFinished(boolean status, long id); + public static native void nativeOnDownloadProgress(long id, long bytesDownloaded, long bytesTotal); } diff --git a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java index 4275038229..c5ea263c89 100644 --- a/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java +++ b/android/src/com/mapswithme/maps/downloader/OnmapDownloader.java @@ -250,7 +250,10 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener { MapManager.nativeUnsubscribe(mStorageSubscriptionSlot); mStorageSubscriptionSlot = 0; + MapManager.nativeUnsubscribeOnCountryChanged(); + + MapDownloadManager.from(mActivity).stopProgressTracking(); } } @@ -259,7 +262,10 @@ public class OnmapDownloader implements MwmActivity.LeftAnimationTrackListener if (mStorageSubscriptionSlot == 0) { mStorageSubscriptionSlot = MapManager.nativeSubscribe(mStorageCallback); + MapManager.nativeSubscribeOnCountryChanged(mCountryChangedListener); + + MapDownloadManager.from(mActivity).startProgressTracking(); } } diff --git a/android/src/com/mapswithme/maps/scheduling/JobIdMap.java b/android/src/com/mapswithme/maps/scheduling/JobIdMap.java index a6b182e8db..7cce57837e 100644 --- a/android/src/com/mapswithme/maps/scheduling/JobIdMap.java +++ b/android/src/com/mapswithme/maps/scheduling/JobIdMap.java @@ -1,6 +1,7 @@ package com.mapswithme.maps.scheduling; import com.mapswithme.maps.background.OsmUploadService; +import com.mapswithme.maps.background.SystemDownloadCompletedService; import com.mapswithme.maps.location.TrackRecorderWakeService; import java.util.HashMap; @@ -12,6 +13,7 @@ public class JobIdMap static { MAP.put(TrackRecorderWakeService.class, calcIdentifier(MAP.size())); + MAP.put(SystemDownloadCompletedService.class, calcIdentifier(MAP.size())); MAP.put(OsmUploadService.class, calcIdentifier(MAP.size())); } diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 785155bce3..6008fbe949 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -47,6 +47,7 @@ import com.mapswithme.maps.bookmarks.data.Metadata; import com.mapswithme.maps.bookmarks.data.RoadWarningMarkType; import com.mapswithme.maps.downloader.CountryItem; import com.mapswithme.maps.downloader.DownloaderStatusIcon; +import com.mapswithme.maps.downloader.MapDownloadManager; import com.mapswithme.maps.downloader.MapManager; import com.mapswithme.maps.editor.Editor; import com.mapswithme.maps.editor.OpeningHours; @@ -1267,6 +1268,7 @@ public class PlacePageView extends NestedScrollViewClickFixed mCurrentCountry = map; if (mStorageCallbackSlot == 0) mStorageCallbackSlot = MapManager.nativeSubscribe(mStorageCallback); + MapDownloadManager.from(getContext()).startProgressTracking(); mDownloaderIcon.setOnIconClickListener(mDownloadClickListener) .setOnCancelClickListener(mCancelDownloadListener); @@ -1282,6 +1284,7 @@ public class PlacePageView extends NestedScrollViewClickFixed MapManager.nativeUnsubscribe(mStorageCallbackSlot); mStorageCallbackSlot = 0; + MapDownloadManager.from(getContext()).stopProgressTracking(); mCurrentCountry = null; mDownloaderIcon.setOnIconClickListener(null) .setOnCancelClickListener(null); diff --git a/generator/transit_generator_experimental.cpp b/generator/transit_generator_experimental.cpp index b24697c704..a0a31884a7 100644 --- a/generator/transit_generator_experimental.cpp +++ b/generator/transit_generator_experimental.cpp @@ -196,11 +196,19 @@ EdgeIdToFeatureId BuildTransit(std::string const & mwmDir, CountryId const & cou data.CheckValid(); data.CheckUnique(); + // Transit graph numerates features according to their orderto their order.. + EdgeIdToFeatureId edgeToFeature; + for (size_t i = 0; i < data.GetEdges().size(); ++i) + { + auto const & e = data.GetEdges()[i]; + EdgeId id(e.GetStop1Id(), e.GetStop2Id(), e.GetLineId()); + edgeToFeature[id] = i; + } + FilesContainerW container(mwmPath, FileWriter::OP_WRITE_EXISTING); auto writer = container.GetWriter(TRANSIT_FILE_TAG); data.Serialize(*writer); - CHECK_EQUAL(data.GetEdgeIdToFeatureId().size(), data.GetEdges().size(), ()); - return data.GetEdgeIdToFeatureId(); + return edgeToFeature; } } // namespace experimental } // namespace transit diff --git a/platform/downloader_utils.cpp b/platform/downloader_utils.cpp index 3a088bd503..084af9e6b7 100644 --- a/platform/downloader_utils.cpp +++ b/platform/downloader_utils.cpp @@ -10,8 +10,6 @@ #include "std/target_os.hpp" -#include - namespace { std::string const kMapsPath = "maps"; @@ -30,16 +28,47 @@ std::string GetFileDownloadUrl(std::string const & fileName, int64_t dataVersion url::UrlEncode(fileName)); } +bool IsUrlSupported(std::string const & url) +{ + auto const urlComponents = strings::Tokenize(url, "/"); + if (urlComponents.empty()) + return false; + + if (urlComponents[0] != kMapsPath && urlComponents[0] != kDiffsPath) + return false; + + if (urlComponents[0] == kMapsPath && urlComponents.size() != 3) + return false; + + if (urlComponents[0] == kDiffsPath && urlComponents.size() != 4) + return false; + + int64_t dataVersion = 0; + if (!strings::to_int64(urlComponents[1], dataVersion)) + return false; + + if (urlComponents[0] == kDiffsPath) + { + int64_t diffVersion = 0; + if (!strings::to_int64(urlComponents[2], diffVersion)) + return false; + } + + auto const countryComponents = strings::Tokenize(url::UrlDecode(urlComponents.back()), "."); + return countryComponents.size() == 2; +} + std::string GetFilePathByUrl(std::string const & url) { auto const urlComponents = strings::Tokenize(url, "/"); - CHECK_GREATER(urlComponents.size(), 1, ()); + CHECK_GREATER(urlComponents.size(), 2, (urlComponents)); + CHECK_LESS(urlComponents.size(), 5, (urlComponents)); int64_t dataVersion = 0; CHECK(strings::to_int64(urlComponents[1], dataVersion), ()); auto const countryComponents = strings::Tokenize(url::UrlDecode(urlComponents.back()), "."); - CHECK(!countryComponents.empty(), ()); + CHECK_EQUAL(countryComponents.size(), 2, ()); auto const fileType = urlComponents[0] == kDiffsPath ? MapFileType::Diff : MapFileType::Map; diff --git a/platform/downloader_utils.hpp b/platform/downloader_utils.hpp index edb8fb9b95..d236fb00e6 100644 --- a/platform/downloader_utils.hpp +++ b/platform/downloader_utils.hpp @@ -6,5 +6,7 @@ namespace downloader { std::string GetFileDownloadUrl(std::string const & fileName, int64_t dataVersion, uint64_t diffVersion = 0); +bool IsUrlSupported(std::string const & url); std::string GetFilePathByUrl(std::string const & url); } // namespace downloader + diff --git a/platform/platform_tests/downloader_utils_tests.cpp b/platform/platform_tests/downloader_utils_tests.cpp index 2140566bbb..82e59a8d31 100644 --- a/platform/platform_tests/downloader_utils_tests.cpp +++ b/platform/platform_tests/downloader_utils_tests.cpp @@ -8,6 +8,7 @@ UNIT_TEST(UrlConversionTest) { { std::string const mwmName = "Luna"; + std::string const fileName = platform::GetFileName(mwmName, MapFileType::Map); int64_t const dataVersion = version::FOR_TESTING_MWM1; int64_t const diffVersion = 0; MapFileType const fileType = MapFileType::Map; @@ -15,13 +16,14 @@ UNIT_TEST(UrlConversionTest) auto const path = platform::GetFileDownloadPath(dataVersion, platform::CountryFile(mwmName), fileType); - auto const url = downloader::GetFileDownloadUrl(mwmName, dataVersion, diffVersion); + auto const url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion); auto const resultPath = downloader::GetFilePathByUrl(url); TEST_EQUAL(path, resultPath, ()); } { std::string const mwmName = "Luna"; + std::string const fileName = platform::GetFileName(mwmName, MapFileType::Diff); int64_t const dataVersion = version::FOR_TESTING_MWM2; int64_t const diffVersion = version::FOR_TESTING_MWM1; MapFileType const fileType = MapFileType::Diff; @@ -29,9 +31,52 @@ UNIT_TEST(UrlConversionTest) auto const path = platform::GetFileDownloadPath(dataVersion, platform::CountryFile(mwmName), fileType); - auto const url = downloader::GetFileDownloadUrl(mwmName, dataVersion, diffVersion); + auto const url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion); auto const resultPath = downloader::GetFilePathByUrl(url); TEST_EQUAL(path, resultPath, ()); } } + +UNIT_TEST(IsUrlSupportedTest) +{ + std::string const mwmName = "Luna"; + + std::string fileName = platform::GetFileName(mwmName, MapFileType::Map); + int64_t dataVersion = version::FOR_TESTING_MWM1; + int64_t diffVersion = 0; + + auto url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion); + TEST(downloader::IsUrlSupported(url), ()); + TEST(downloader::IsUrlSupported("maps/991215/Luna.mwm"), ()); + TEST(downloader::IsUrlSupported("maps/0/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("maps/x/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("macarena/0/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("/hack/maps/0/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("0/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("maps/0/Luna"), ()); + TEST(!downloader::IsUrlSupported("0/Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("Luna.mwm"), ()); + TEST(!downloader::IsUrlSupported("Luna"), ()); + + fileName = platform::GetFileName(mwmName, MapFileType::Diff); + diffVersion = version::FOR_TESTING_MWM1; + url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion); + TEST(downloader::IsUrlSupported(url), ()); + TEST(downloader::IsUrlSupported("diffs/991215/991215/Luna.mwmdiff"), ()); + TEST(downloader::IsUrlSupported("diffs/0/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/x/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/0/x/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/x/x/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("beefs/0/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/0/0/Luna.mwmdiff.f"), ()); + TEST(!downloader::IsUrlSupported("maps/diffs/0/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/0/0/Luna"), ()); + TEST(!downloader::IsUrlSupported("0/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/0"), ()); + TEST(!downloader::IsUrlSupported("diffs/0/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("diffs/Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("Luna.mwmdiff"), ()); + TEST(!downloader::IsUrlSupported("Luna"), ()); +} diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 4d84384136..cab0fe7c1b 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -801,7 +801,7 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::Joints); + FindPath(params, {} /* mwmIds */, routingResult); if (result != RouterResultCode::NoError) return result; @@ -827,8 +827,7 @@ RouterResultCode IndexRouter::CalculateSubrouteNoLeapsMode( RoutingResult routingResult; set const mwmIds = starter.GetMwms(); - RouterResultCode const result = - FindPath(params, mwmIds, routingResult, WorldGraphMode::NoLeaps); + RouterResultCode const result = FindPath(params, mwmIds, routingResult); if (result != RouterResultCode::NoError) return result; @@ -863,7 +862,7 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::LeapsOnly); + FindPath(params, {} /* mwmIds */, routingResult); progress->PushAndDropLastSubProgress(); @@ -1351,7 +1350,7 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, nullptr /* prevRoute */, delegate.GetCancellable(), move(visitor), AStarLengthChecker(starter)); - resultCode = FindPath(params, mwmIds, routingResult, mode); + resultCode = FindPath(params, mwmIds, routingResult); return resultCode; }; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 8443991451..f5b5ecec1e 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -207,9 +207,8 @@ private: } template - RouterResultCode FindPath( - AStarParams & params, std::set const & mwmIds, - RoutingResult & routingResult, WorldGraphMode mode) const + RouterResultCode FindPath(AStarParams & params, std::set const & mwmIds, + RoutingResult & routingResult) const { AStarAlgorithm algorithm; return ConvertTransitResult( diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt index bc40791d9d..5110efcc74 100644 --- a/storage/CMakeLists.txt +++ b/storage/CMakeLists.txt @@ -28,7 +28,7 @@ set( diff_scheme/diff_types.hpp downloader.hpp downloader_search_params.hpp - downloader_queue.hpp + downloader_queue_interface.hpp downloader_queue_universal.cpp downloader_queue_universal.hpp downloading_policy.cpp @@ -51,13 +51,17 @@ set( storage_helpers.hpp ) -if (${PLATFORM_IPHONE}) +if(${PLATFORM_ANDROID}) + append( + SRC + background_downloading/downloader_queue.hpp + ) +elseif(${PLATFORM_IPHONE}) append( SRC background_downloading/downloader_adapter_ios.h background_downloading/downloader_adapter_ios.mm - background_downloading/downloader_queue_ios.hpp - background_downloading/downloader_queue_ios.cpp + background_downloading/downloader_queue.hpp ) else() append( diff --git a/storage/background_downloading/downloader_adapter_ios.h b/storage/background_downloading/downloader_adapter_ios.h index f0bc17c74b..e0e39e2a5d 100644 --- a/storage/background_downloading/downloader_adapter_ios.h +++ b/storage/background_downloading/downloader_adapter_ios.h @@ -1,6 +1,6 @@ #pragma once -#include "storage/background_downloading/downloader_queue_ios.hpp" +#include "storage/background_downloading/downloader_queue.hpp" #include "storage/map_files_downloader_with_ping.hpp" namespace storage @@ -20,9 +20,9 @@ private: void Download(QueuedCountry && queuedCountry) override; // Trying to download mwm from different servers recursively. - void DownloadFromAnyUrl(CountryId const & countryId, std::string const & downloadPath, - std::vector const & urls); + void DownloadFromLastUrl(CountryId const & countryId, std::string const & downloadPath, + std::vector const & urls); - BackgroundDownloaderQueue m_queue; + BackgroundDownloaderQueue m_queue; }; } // namespace storage diff --git a/storage/background_downloading/downloader_adapter_ios.mm b/storage/background_downloading/downloader_adapter_ios.mm index e1cc1460eb..2a7be85f4f 100644 --- a/storage/background_downloading/downloader_adapter_ios.mm +++ b/storage/background_downloading/downloader_adapter_ios.mm @@ -36,7 +36,7 @@ void BackgroundDownloaderAdapter::Remove(CountryId const & countryId) return; BackgroundDownloader * downloader = [BackgroundDownloader sharedBackgroundMapDownloader]; - auto const taskIdentifier = m_queue.GetTaskIdByCountryId(countryId); + auto const taskIdentifier = m_queue.GetTaskInfoForCountryId(countryId); if (taskIdentifier) [downloader cancelTaskWithIdentifier:*taskIdentifier]; m_queue.Remove(countryId); @@ -69,16 +69,16 @@ void BackgroundDownloaderAdapter::Download(QueuedCountry && queuedCountry) auto const countryId = queuedCountry.GetCountryId(); auto const urls = MakeUrlList(queuedCountry.GetRelativeUrl()); - auto const path = queuedCountry.GetFileDownloadPath(); + m_queue.Append(std::move(queuedCountry)); - DownloadFromAnyUrl(countryId, path, urls); + DownloadFromLastUrl(countryId, path, urls); } -void BackgroundDownloaderAdapter::DownloadFromAnyUrl(CountryId const & countryId, - std::string const & downloadPath, - std::vector const & urls) +void BackgroundDownloaderAdapter::DownloadFromLastUrl(CountryId const & countryId, + std::string const & downloadPath, + std::vector const & urls) { if (urls.empty()) return; @@ -92,7 +92,7 @@ void BackgroundDownloaderAdapter::DownloadFromAnyUrl(CountryId const & countryId if (status == downloader::DownloadStatus::Failed && urls.size() > 1) { urls.pop_back(); - DownloadFromAnyUrl(countryId, downloadPath, urls); + DownloadFromLastUrl(countryId, downloadPath, urls); } else { @@ -115,7 +115,7 @@ void BackgroundDownloaderAdapter::DownloadFromAnyUrl(CountryId const & countryId BackgroundDownloader * downloader = [BackgroundDownloader sharedBackgroundMapDownloader]; NSUInteger taskId = [downloader downloadWithUrl:url completion:onFinish progress:onProgress]; - m_queue.SetTaskIdForCountryId(countryId, taskId); + m_queue.SetTaskInfoForCountryId(countryId, taskId); } std::unique_ptr GetDownloader() diff --git a/storage/background_downloading/downloader_queue.hpp b/storage/background_downloading/downloader_queue.hpp new file mode 100644 index 0000000000..b8a70ce1f5 --- /dev/null +++ b/storage/background_downloading/downloader_queue.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include "storage/downloader_queue_interface.hpp" +#include "storage/queued_country.hpp" +#include "storage/storage_defines.hpp" + +#include +#include +#include +#include +#include + +namespace storage +{ +template +class BackgroundDownloaderQueue : public QueueInterface +{ +public: + using ForEachTaskInfoTypeFunction = std::function; + + bool IsEmpty() const override + { + return m_queue.empty(); + } + + size_t Count() const override + { + return m_queue.size(); + } + + bool Contains(CountryId const & country) const override + { + return m_queue.find(country) != m_queue.cend(); + } + + void ForEachCountry(ForEachCountryFunction const & fn) const override + { + for (auto const & item : m_queue) + { + fn(item.second.m_queuedCountry); + } + } + + void ForEachTaskInfo(ForEachTaskInfoTypeFunction const & fn) const + { + for (auto const & item : m_queue) + { + if (item.second.m_taskInfo) + fn(*item.second.m_taskInfo); + } + } + + void Append(QueuedCountry && country) + { + auto const countryId = country.GetCountryId(); + auto const result = m_queue.emplace(countryId, std::move(country)); + result.first->second.m_queuedCountry.OnCountryInQueue(); + } + + void SetTaskInfoForCountryId(CountryId const & countryId, TaskInfo const & taskInfo) + { + auto const it = m_queue.find(countryId); + CHECK(it != m_queue.cend(), ()); + + it->second.m_taskInfo = taskInfo; + } + + std::optional GetTaskInfoForCountryId(CountryId const & countryId) const + { + auto const it = m_queue.find(countryId); + if (it == m_queue.cend()) + return {}; + + return it->second.m_taskInfo; + } + + QueuedCountry & GetCountryById(CountryId const & countryId) + { + return m_queue.at(countryId).m_queuedCountry; + } + + void Remove(CountryId const & countryId) + { + m_queue.erase(countryId); + } + + void Clear() + { + m_queue.clear(); + } + +private: + struct TaskData + { + explicit TaskData(QueuedCountry && country) : m_queuedCountry(std::move(country)) {} + + QueuedCountry m_queuedCountry; + std::optional m_taskInfo; + }; + + std::unordered_map m_queue; +}; +} // namespace storage diff --git a/storage/background_downloading/downloader_queue_ios.cpp b/storage/background_downloading/downloader_queue_ios.cpp deleted file mode 100644 index 3796bafa09..0000000000 --- a/storage/background_downloading/downloader_queue_ios.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#import "storage/background_downloading/downloader_queue_ios.hpp" - -#include - -namespace storage -{ -bool BackgroundDownloaderQueue::IsEmpty() const -{ - return m_queue.empty(); -} - -bool BackgroundDownloaderQueue::Contains(CountryId const & country) const -{ - return m_queue.find(country) != m_queue.cend(); -} - -void BackgroundDownloaderQueue::ForEachCountry(ForEachCountryFunction const & fn) const -{ - for (auto const & item : m_queue) - { - fn(item.second.m_queuedCountry); - } -} - -void BackgroundDownloaderQueue::SetTaskIdForCountryId(CountryId const & country, uint64_t taskId) -{ - auto const it = m_queue.find(country); - CHECK(it != m_queue.cend(), ()); - - it->second.m_taskId = taskId; -} - -std::optional BackgroundDownloaderQueue::GetTaskIdByCountryId(CountryId const & country) const -{ - auto const it = m_queue.find(country); - if (it == m_queue.cend()) - return {}; - - return it->second.m_taskId; -} - -QueuedCountry & BackgroundDownloaderQueue::GetCountryById(CountryId const & countryId) -{ - return m_queue.at(countryId).m_queuedCountry; -} - -void BackgroundDownloaderQueue::Remove(CountryId const & countryId) -{ - m_queue.erase(countryId); -} - -void BackgroundDownloaderQueue::Clear() -{ - m_queue.clear(); -} -} // namespace storage diff --git a/storage/background_downloading/downloader_queue_ios.hpp b/storage/background_downloading/downloader_queue_ios.hpp deleted file mode 100644 index 62d5778091..0000000000 --- a/storage/background_downloading/downloader_queue_ios.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "storage/downloader_queue.hpp" -#include "storage/queued_country.hpp" -#include "storage/storage_defines.hpp" - -#include -#include -#include -#include - -namespace storage -{ -class BackgroundDownloaderQueue : public QueueInterface -{ -public: - bool IsEmpty() const override; - bool Contains(CountryId const & country) const override; - void ForEachCountry(ForEachCountryFunction const & fn) const override; - - void Append(QueuedCountry && country) - { - auto const countryId = country.GetCountryId(); - auto const result = m_queue.emplace(countryId, std::move(country)); - result.first->second.m_queuedCountry.OnCountryInQueue(); - } - - void SetTaskIdForCountryId(CountryId const & countryId, uint64_t taskId); - std::optional GetTaskIdByCountryId(CountryId const & countryId) const; - - QueuedCountry & GetCountryById(CountryId const & countryId); - - void Remove(CountryId const & country); - void Clear(); - -private: - struct TaskData - { - explicit TaskData(QueuedCountry && country) : m_queuedCountry(std::move(country)) {} - - QueuedCountry m_queuedCountry; - std::optional m_taskId; - }; - - std::unordered_map m_queue; -}; -} // namespace storage diff --git a/storage/downloader_queue.hpp b/storage/downloader_queue_interface.hpp similarity index 92% rename from storage/downloader_queue.hpp rename to storage/downloader_queue_interface.hpp index 47f1ac8ae7..9b0aa6147d 100644 --- a/storage/downloader_queue.hpp +++ b/storage/downloader_queue_interface.hpp @@ -11,6 +11,7 @@ public: using ForEachCountryFunction = std::function; virtual bool IsEmpty() const = 0; + virtual size_t Count() const = 0; virtual bool Contains(CountryId const & country) const = 0; virtual void ForEachCountry(ForEachCountryFunction const & fn) const = 0; }; diff --git a/storage/downloader_queue_universal.cpp b/storage/downloader_queue_universal.cpp index 4fa9e4f1f6..614c01ae63 100644 --- a/storage/downloader_queue_universal.cpp +++ b/storage/downloader_queue_universal.cpp @@ -9,6 +9,11 @@ bool Queue::IsEmpty() const return m_queue.empty(); } +size_t Queue::Count() const +{ + return m_queue.size(); +} + bool Queue::Contains(CountryId const & country) const { return std::find(m_queue.cbegin(), m_queue.cend(), country) != m_queue.cend(); @@ -68,9 +73,4 @@ void Queue::Clear() { m_queue.clear(); } - -size_t Queue::Count() const -{ - return m_queue.size(); -} } // namespace storage diff --git a/storage/downloader_queue_universal.hpp b/storage/downloader_queue_universal.hpp index dbdb2c55c8..0589f0515a 100644 --- a/storage/downloader_queue_universal.hpp +++ b/storage/downloader_queue_universal.hpp @@ -1,6 +1,6 @@ #pragma once -#include "storage/downloader_queue.hpp" +#include "storage/downloader_queue_interface.hpp" #include "storage/queued_country.hpp" #include "storage/storage_defines.hpp" @@ -16,6 +16,7 @@ public: // QueueInterface overrides: bool IsEmpty() const override; + size_t Count() const override; bool Contains(CountryId const & country) const override; void ForEachCountry(ForEachCountryFunction const & fn) const override; @@ -30,8 +31,6 @@ public: void Remove(CountryId const & country); void Clear(); - size_t Count() const; - private: std::list m_queue; }; diff --git a/storage/pinger.cpp b/storage/pinger.cpp index 3811f4c1f8..c483330092 100644 --- a/storage/pinger.cpp +++ b/storage/pinger.cpp @@ -8,38 +8,39 @@ #include "base/stl_helpers.hpp" #include "base/thread_pool_delayed.hpp" +#include +#include -#include -#include using namespace std; +using namespace std::chrono; namespace { auto constexpr kTimeoutInSeconds = 5.0; -void DoPing(string const & url, size_t index, vector & readyUrls) +int32_t DoPing(string const & url) { if (url.empty()) { ASSERT(false, ("Metaserver returned an empty url.")); - return; + return -1; } platform::HttpClient request(url); request.SetHttpMethod("HEAD"); request.SetTimeout(kTimeoutInSeconds); + auto const begin = high_resolution_clock::now(); if (request.RunHttpRequest() && !request.WasRedirected() && request.ErrorCode() == 200) { - readyUrls[index] = url; + return duration_cast(high_resolution_clock::now() - begin).count(); } else { - ostringstream ost; - ost << "Request to server " << url << " failed. Code = " << request.ErrorCode() - << ", redirection = " << request.WasRedirected(); - LOG(LINFO, (ost.str())); + LOG(LWARNING, ("Request to server", url, "failed with code =", request.ErrorCode(), "; redirection =", request.WasRedirected())); } + + return -1; } } // namespace @@ -48,19 +49,29 @@ namespace storage // static Pinger::Endpoints Pinger::ExcludeUnavailableEndpoints(Endpoints const & urls) { + using base::thread_pool::delayed::ThreadPool; + auto const size = urls.size(); CHECK_GREATER(size, 0, ()); - vector readyUrls(size); + map timeUrls; { - base::thread_pool::delayed::ThreadPool t(size); + ThreadPool t(size, ThreadPool::Exit::ExecPending); for (size_t i = 0; i < size; ++i) - t.Push([url = urls[i], &readyUrls, i] { DoPing(url, i, readyUrls); }); - - t.Shutdown(base::thread_pool::delayed::ThreadPool::Exit::ExecPending); + { + t.Push([&urls, &timeUrls, i] + { + auto const pingTime = DoPing(urls[i]); + if (pingTime > 0) + timeUrls[pingTime] = i; + }); + } } - base::EraseIf(readyUrls, [](auto const & url) { return url.empty(); }); + Endpoints readyUrls; + for (auto const & [_, index] : timeUrls) + readyUrls.push_back(urls[index]); + return readyUrls; } } // namespace storage diff --git a/transit/transit_schedule.cpp b/transit/transit_schedule.cpp index 37e581627b..a0c26bc4cf 100644 --- a/transit/transit_schedule.cpp +++ b/transit/transit_schedule.cpp @@ -277,8 +277,10 @@ FrequencyIntervals::FrequencyIntervals(gtfs::Frequencies const & frequencies) { for (auto const & freq : frequencies) { - CHECK_GREATER(freq.headway_secs, 0, ()); - m_intervals.emplace(TimeInterval(freq.start_time, freq.end_time), freq.headway_secs); + if (freq.headway_secs > 0) + m_intervals.emplace(TimeInterval(freq.start_time, freq.end_time), freq.headway_secs); + else + LOG(LINFO, ("Bad headway_secs:", freq.headway_secs)); } } diff --git a/transit/world_feed/world_feed.cpp b/transit/world_feed/world_feed.cpp index 9a1b956087..3c655d15a2 100644 --- a/transit/world_feed/world_feed.cpp +++ b/transit/world_feed/world_feed.cpp @@ -475,7 +475,11 @@ bool WorldFeed::FillRoutes() continue; auto const itAgencyHash = m_gtfsIdToHash[FieldIdx::AgencyIdx].find(route.agency_id); - CHECK(itAgencyHash != m_gtfsIdToHash[FieldIdx::AgencyIdx].end(), (route.agency_id)); + if (itAgencyHash == m_gtfsIdToHash[FieldIdx::AgencyIdx].end()) + { + LOG(LINFO, ("Unknown agency", route.agency_id)); + continue; + } auto const & agencyHash = itAgencyHash->second; CHECK(!agencyHash.empty(), ("Empty hash for agency id:", route.agency_id)); @@ -927,7 +931,7 @@ std::optional WorldFeed::ProjectStopsToShape( CHECK(itStop != m_stops.m_data.end(), (stopId)); auto const & stop = itStop->second; - size_t const prevIdx = i == 0 ? (direction == Direction::Forward ? 0 : shape.size()) + size_t const prevIdx = i == 0 ? (direction == Direction::Forward ? 0 : shape.size() - 1) : stopsToIndexes[stopIds[i - 1]].back(); auto const [curIdx, pointInserted] = PrepareNearestPointOnTrack(stop.m_point, prevPoint, prevIdx, direction, shape); @@ -1932,12 +1936,13 @@ void WorldFeed::SplitLinesBasedData() auto const edgeFirst = m_edges.m_data.at( EdgeId(lineData.m_stopIds[firstStopIdx], lineData.m_stopIds[firstStopIdx + 1], lineId)); - CHECK(!(edgeFirst.m_shapeLink.m_startIndex == 0 && edgeFirst.m_shapeLink.m_endIndex == 0), - ()); + if (edgeFirst.m_shapeLink.m_startIndex == 0 && edgeFirst.m_shapeLink.m_endIndex == 0) + continue; auto const edgeLast = m_edges.m_data.at( EdgeId(lineData.m_stopIds[lastStopIdx - 1], lineData.m_stopIds[lastStopIdx], lineId)); - CHECK(!(edgeLast.m_shapeLink.m_startIndex == 0 && edgeLast.m_shapeLink.m_endIndex == 0), ()); + if (edgeLast.m_shapeLink.m_startIndex == 0 && edgeLast.m_shapeLink.m_endIndex == 0) + continue; lineInRegion.m_shapeLink.m_startIndex = std::min(edgeFirst.m_shapeLink.m_startIndex, edgeLast.m_shapeLink.m_endIndex); diff --git a/xcode/storage/storage.xcodeproj/project.pbxproj b/xcode/storage/storage.xcodeproj/project.pbxproj index d440e9ab82..fa81a058d1 100644 --- a/xcode/storage/storage.xcodeproj/project.pbxproj +++ b/xcode/storage/storage.xcodeproj/project.pbxproj @@ -23,6 +23,8 @@ 3971141E229D89F4003915E5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3971141D229D89F4003915E5 /* Security.framework */; }; 39B9682223E1AC5F00D3B8E3 /* libge0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B9682123E1AC5F00D3B8E3 /* libge0.a */; }; 39B9682B23E1AD9F00D3B8E3 /* libge0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 39B9682123E1AC5F00D3B8E3 /* libge0.a */; }; + 3D2D57B725ADCEEB0002D8E3 /* downloader_queue_interface.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D2D57B625ADCEEB0002D8E3 /* downloader_queue_interface.hpp */; }; + 3D2D57BB25B6E8550002D8E3 /* downloader_queue.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D2D57BA25B6E8550002D8E3 /* downloader_queue.hpp */; }; 3D497EA123292706000FB57D /* map_files_downloader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D497E9E23292706000FB57D /* map_files_downloader.cpp */; }; 3D497EA223292706000FB57D /* map_files_downloader_with_ping.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D497E9F23292706000FB57D /* map_files_downloader_with_ping.hpp */; }; 3D497EA323292706000FB57D /* map_files_downloader_with_ping.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D497EA023292706000FB57D /* map_files_downloader_with_ping.cpp */; }; @@ -38,11 +40,8 @@ 3DFACF3F243C985A00A29A94 /* downloader.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFACF3B243C985A00A29A94 /* downloader.hpp */; }; 3DFACF40243C985A00A29A94 /* downloader_queue_universal.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFACF3C243C985A00A29A94 /* downloader_queue_universal.hpp */; }; 3DFACF41243C985A00A29A94 /* downloader_queue_universal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFACF3D243C985A00A29A94 /* downloader_queue_universal.cpp */; }; - 3DFACF42243C985A00A29A94 /* downloader_queue.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFACF3E243C985A00A29A94 /* downloader_queue.hpp */; }; - 3DFACF4E243CB1AB00A29A94 /* downloader_queue_ios.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3DFACF46243CB1AB00A29A94 /* downloader_queue_ios.hpp */; }; 3DFACF4F243CB1AB00A29A94 /* downloader_adapter_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DFACF47243CB1AB00A29A94 /* downloader_adapter_ios.h */; }; 3DFACF51243CB1AB00A29A94 /* downloader_adapter_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3DFACF49243CB1AB00A29A94 /* downloader_adapter_ios.mm */; }; - 3DFACF53243CB1AB00A29A94 /* downloader_queue_ios.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3DFACF4B243CB1AB00A29A94 /* downloader_queue_ios.cpp */; }; 401ECED41F56C50900DFDF76 /* country_parent_getter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 401ECED21F56C50900DFDF76 /* country_parent_getter.cpp */; }; 401ECED51F56C50900DFDF76 /* country_parent_getter.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 401ECED31F56C50900DFDF76 /* country_parent_getter.hpp */; }; 402873422295A91F0036AA1C /* country_tree.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4028733F2295A91E0036AA1C /* country_tree.hpp */; }; @@ -239,6 +238,8 @@ 3971141B229D8983003915E5 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 3971141D229D89F4003915E5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 39B9682123E1AC5F00D3B8E3 /* libge0.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libge0.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3D2D57B625ADCEEB0002D8E3 /* downloader_queue_interface.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader_queue_interface.hpp; sourceTree = ""; }; + 3D2D57BA25B6E8550002D8E3 /* downloader_queue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader_queue.hpp; sourceTree = ""; }; 3D497E9E23292706000FB57D /* map_files_downloader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = map_files_downloader.cpp; sourceTree = ""; }; 3D497E9F23292706000FB57D /* map_files_downloader_with_ping.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = map_files_downloader_with_ping.hpp; sourceTree = ""; }; 3D497EA023292706000FB57D /* map_files_downloader_with_ping.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = map_files_downloader_with_ping.cpp; sourceTree = ""; }; @@ -254,11 +255,8 @@ 3DFACF3B243C985A00A29A94 /* downloader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader.hpp; sourceTree = ""; }; 3DFACF3C243C985A00A29A94 /* downloader_queue_universal.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader_queue_universal.hpp; sourceTree = ""; }; 3DFACF3D243C985A00A29A94 /* downloader_queue_universal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = downloader_queue_universal.cpp; sourceTree = ""; }; - 3DFACF3E243C985A00A29A94 /* downloader_queue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader_queue.hpp; sourceTree = ""; }; - 3DFACF46243CB1AB00A29A94 /* downloader_queue_ios.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = downloader_queue_ios.hpp; sourceTree = ""; }; 3DFACF47243CB1AB00A29A94 /* downloader_adapter_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = downloader_adapter_ios.h; sourceTree = ""; }; 3DFACF49243CB1AB00A29A94 /* downloader_adapter_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = downloader_adapter_ios.mm; sourceTree = ""; }; - 3DFACF4B243CB1AB00A29A94 /* downloader_queue_ios.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = downloader_queue_ios.cpp; sourceTree = ""; }; 401ECED21F56C50900DFDF76 /* country_parent_getter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = country_parent_getter.cpp; sourceTree = ""; }; 401ECED31F56C50900DFDF76 /* country_parent_getter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = country_parent_getter.hpp; sourceTree = ""; }; 4028733F2295A91E0036AA1C /* country_tree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = country_tree.hpp; sourceTree = ""; }; @@ -516,10 +514,9 @@ 3DFACF43243CB1AB00A29A94 /* background_downloading */ = { isa = PBXGroup; children = ( - 3DFACF46243CB1AB00A29A94 /* downloader_queue_ios.hpp */, + 3D2D57BA25B6E8550002D8E3 /* downloader_queue.hpp */, 3DFACF47243CB1AB00A29A94 /* downloader_adapter_ios.h */, 3DFACF49243CB1AB00A29A94 /* downloader_adapter_ios.mm */, - 3DFACF4B243CB1AB00A29A94 /* downloader_queue_ios.cpp */, ); path = background_downloading; sourceTree = ""; @@ -622,9 +619,9 @@ isa = PBXGroup; children = ( 3DFACF43243CB1AB00A29A94 /* background_downloading */, + 3D2D57B625ADCEEB0002D8E3 /* downloader_queue_interface.hpp */, 3DFACF3D243C985A00A29A94 /* downloader_queue_universal.cpp */, 3DFACF3C243C985A00A29A94 /* downloader_queue_universal.hpp */, - 3DFACF3E243C985A00A29A94 /* downloader_queue.hpp */, 3DFACF3B243C985A00A29A94 /* downloader.hpp */, 402AD2E324A200F600DE5CB1 /* country_tree_helpers.cpp */, 402AD2E224A200F600DE5CB1 /* country_tree_helpers.hpp */, @@ -710,15 +707,16 @@ 674125201B4C05FA00A3E828 /* map_files_downloader.hpp in Headers */, 56D8CB9A1CAC17A80003F420 /* test_defines.hpp in Headers */, F68CC5C01F38B967007527C7 /* diff_types.hpp in Headers */, + 3D2D57BB25B6E8550002D8E3 /* downloader_queue.hpp in Headers */, 3DFACF3F243C985A00A29A94 /* downloader.hpp in Headers */, 402AD2E424A200F600DE5CB1 /* country_tree_helpers.hpp in Headers */, + 3D2D57B725ADCEEB0002D8E3 /* downloader_queue_interface.hpp in Headers */, 401ECED51F56C50900DFDF76 /* country_parent_getter.hpp in Headers */, 3DF528E22386B966000ED0D5 /* diffs_data_source.hpp in Headers */, 34C9BCFD1C6CCF85000DC38D /* country_name_getter.hpp in Headers */, 3DFACF40243C985A00A29A94 /* downloader_queue_universal.hpp in Headers */, 67BE1DC61CD2180D00572709 /* downloading_policy.hpp in Headers */, 675343191A3F5A2600A0A8C3 /* storage_defines.hpp in Headers */, - 3DFACF4E243CB1AB00A29A94 /* downloader_queue_ios.hpp in Headers */, 3DF528E12386B966000ED0D5 /* apply_diff.hpp in Headers */, 67247FD41C60BA8A00EDE56A /* task_runner.hpp in Headers */, 56D0E4801F8E40340084B18C /* routing_helpers.hpp in Headers */, @@ -730,7 +728,6 @@ 6753430A1A3F5A2600A0A8C3 /* country_decl.hpp in Headers */, 67247FCF1C60BA8A00EDE56A /* fake_map_files_downloader.hpp in Headers */, 3DFACF4F243CB1AB00A29A94 /* downloader_adapter_ios.h in Headers */, - 3DFACF42243C985A00A29A94 /* downloader_queue.hpp in Headers */, 67AF4A011BC579DD0048B1ED /* country_info_getter.hpp in Headers */, 3DD84014233A38DE0008D0ED /* diff_scheme_loader.hpp in Headers */, 3971140A229D7C6D003915E5 /* helpers.hpp in Headers */, @@ -916,7 +913,6 @@ 402AD2E524A200F600DE5CB1 /* country_tree_helpers.cpp in Sources */, 402873432295A91F0036AA1C /* country_tree.cpp in Sources */, F6BC312C2034366100F677FE /* pinger.cpp in Sources */, - 3DFACF53243CB1AB00A29A94 /* downloader_queue_ios.cpp in Sources */, 34C9BCFC1C6CCF85000DC38D /* country_name_getter.cpp in Sources */, 3DF528E32386B966000ED0D5 /* diffs_data_source.cpp in Sources */, );