#include "offline_manager.hpp" #include #include #include #include "attach_env.hpp" namespace mbgl { namespace android { namespace { // Reattach, the callback comes from a different thread void handleException(std::exception_ptr exception, const jni::Object& callback, android::UniqueEnv env = android::AttachEnv()) { if (exception) { OfflineManager::FileSourceCallback::onError( *env, callback, jni::Make(*env, mbgl::util::toString(exception))); } else { OfflineManager::FileSourceCallback::onSuccess(*env, callback); } } } // namespace // OfflineManager // OfflineManager::OfflineManager(jni::JNIEnv& env, const jni::Object& jFileSource) : fileSource(std::static_pointer_cast(std::shared_ptr( mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, FileSource::getSharedResourceOptions(env, jFileSource), FileSource::getSharedClientOptions(env, jFileSource))))) { if (!fileSource) { ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Offline functionality is disabled."); } } OfflineManager::~OfflineManager() {} void OfflineManager::setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit) { fileSource->setOfflineMapboxTileCountLimit(limit); } void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, const jni::Object& jFileSource_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); auto globalFilesource = jni::NewGlobal(env_, jFileSource_); fileSource->listOfflineRegions( [ // Keep a shared ptr to a global reference of the callback and file // source so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback)), jFileSource = std::make_shared(std::move(globalFilesource))]( mbgl::expected regions) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (regions) { OfflineManager::ListOfflineRegionsCallback::onList(*env, *jFileSource, *callback, *regions); } else { OfflineManager::ListOfflineRegionsCallback::onError(*env, *callback, regions.error()); } }); } void OfflineManager::getOfflineRegion(jni::JNIEnv& env_, const jni::Object& jFileSource_, const int64_t regionID, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); auto globalFilesource = jni::NewGlobal(env_, jFileSource_); fileSource->getOfflineRegion( regionID, [ // Keep a shared ptr to a global reference of the callback and file // source so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback)), jFileSource = std::make_shared(std::move(globalFilesource)), regionID](mbgl::expected, std::exception_ptr> result) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (result.has_exception() || result.has_exception()) { OfflineManager::GetOfflineRegionCallback::onError(*env, *callback, result.error()); } else { auto region = result.value(); if (region) { OfflineManager::GetOfflineRegionCallback::onRegion(*env, *jFileSource, *callback, *region); } else { OfflineManager::GetOfflineRegionCallback::onRegionNotFound(*env, *jFileSource, *callback); } } }); } void OfflineManager::createOfflineRegion(jni::JNIEnv& env_, const jni::Object& jFileSource_, const jni::Object& definition_, const jni::Array& metadata_, const jni::Object& callback_) { // Convert auto definition = OfflineRegionDefinition::getDefinition(env_, definition_); mbgl::OfflineRegionMetadata metadata; if (metadata_) { metadata = OfflineRegion::metadata(env_, metadata_); } auto globalCallback = jni::NewGlobal(env_, callback_); auto globalFilesource = jni::NewGlobal(env_, jFileSource_); // Create region fileSource->createOfflineRegion( definition, metadata, [ // Keep a shared ptr to a global reference of the callback and file // source so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback)), jFileSource = std::make_shared(std::move(globalFilesource))]( mbgl::expected region) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (region) { OfflineManager::CreateOfflineRegionCallback::onCreate(*env, *jFileSource, *callback, *region); } else { OfflineManager::CreateOfflineRegionCallback::onError(*env, *callback, region.error()); } }); } void OfflineManager::mergeOfflineRegions(jni::JNIEnv& env_, const jni::Object& jFileSource_, const jni::String& jString_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); auto globalFilesource = jni::NewGlobal(env_, jFileSource_); auto path = jni::Make(env_, jString_); fileSource->mergeOfflineRegions( path, [ // Keep a shared ptr to a global reference of the callback and file // source so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback)), jFileSource = std::make_shared(std::move(globalFilesource))]( mbgl::expected regions) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (regions) { OfflineManager::MergeOfflineRegionsCallback::onMerge(*env, *jFileSource, *callback, *regions); } else { OfflineManager::MergeOfflineRegionsCallback::onError(*env, *callback, regions.error()); } }); } void OfflineManager::resetDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->resetDatabase([ // Keep a shared ptr to a global reference of the // callback so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback))]( std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::packDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->packDatabase([ // Keep a shared ptr to a global reference of the // callback so they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback))]( std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::invalidateAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->invalidateAmbientCache( [ // Keep a shared ptr to a global reference of the callback so // they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback))]( std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::clearAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->clearAmbientCache([ // Keep a shared ptr to a global reference of the callback so // they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback))]( std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::setMaximumAmbientCacheSize(jni::JNIEnv& env_, const jni::jlong size_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->setMaximumAmbientCacheSize( size_, [ // Keep a shared ptr to a global reference of the callback so // they are not GC'd in the meanwhile callback = std::make_shared(std::move(globalCallback))]( std::exception_ptr exception) mutable { handleException(exception, *callback); }); } void OfflineManager::runPackDatabaseAutomatically(jni::JNIEnv&, jboolean autopack) { fileSource->runPackDatabaseAutomatically(autopack); } // FileSource::FileSourceCallback // void OfflineManager::FileSourceCallback::onSuccess(jni::JNIEnv& env, const jni::Object& callback) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onSuccess"); callback.Call(env, method); } void OfflineManager::FileSourceCallback::onError(jni::JNIEnv& env, const jni::Object& callback, const jni::String& message) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, message); } void OfflineManager::registerNative(jni::JNIEnv& env) { jni::Class::Singleton(env); jni::Class::Singleton(env); jni::Class::Singleton(env); jni::Class::Singleton(env); jni::Class::Singleton(env); static auto& javaClass = jni::Class::Singleton(env); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) jni::RegisterNativePeer( env, javaClass, "nativePtr", jni::MakePeer&>, "initialize", "finalize", METHOD(&OfflineManager::setOfflineMapboxTileCountLimit, "setOfflineMapboxTileCountLimit"), METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"), METHOD(&OfflineManager::getOfflineRegion, "getOfflineRegion"), METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"), METHOD(&OfflineManager::mergeOfflineRegions, "mergeOfflineRegions"), METHOD(&OfflineManager::resetDatabase, "nativeResetDatabase"), METHOD(&OfflineManager::packDatabase, "nativePackDatabase"), METHOD(&OfflineManager::invalidateAmbientCache, "nativeInvalidateAmbientCache"), METHOD(&OfflineManager::clearAmbientCache, "nativeClearAmbientCache"), METHOD(&OfflineManager::setMaximumAmbientCacheSize, "nativeSetMaximumAmbientCacheSize"), METHOD(&OfflineManager::runPackDatabaseAutomatically, "runPackDatabaseAutomatically"), METHOD(&OfflineManager::putResourceWithUrl, "putResourceWithUrl")); } // OfflineManager::ListOfflineRegionsCallback // void OfflineManager::ListOfflineRegionsCallback::onError( jni::JNIEnv& env, const jni::Object& callback, std::exception_ptr error) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); } void OfflineManager::ListOfflineRegionsCallback::onList( jni::JNIEnv& env, const jni::Object& jFileSource, const jni::Object& callback, mbgl::OfflineRegions& regions) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod>)>(env, "onList"); std::size_t index = 0; auto jregions = jni::Array>::New(env, regions.size()); for (auto& region : regions) { jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region))); index++; } callback.Call(env, method, jregions); } // OfflineManager::GetOfflineRegionCallback // void OfflineManager::GetOfflineRegionCallback::onError( jni::JNIEnv& env, const jni::Object& callback, std::exception_ptr error) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); } void OfflineManager::GetOfflineRegionCallback::onRegionNotFound( jni::JNIEnv& env, const jni::Object& jFileSource, const jni::Object& callback) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onRegionNotFound"); callback.Call(env, method); } void OfflineManager::GetOfflineRegionCallback::onRegion( jni::JNIEnv& env, const jni::Object& jFileSource, const jni::Object& callback, mbgl::OfflineRegion& region) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod)>(env, "onRegion"); callback.Call(env, method, OfflineRegion::New(env, jFileSource, std::move(region))); } // OfflineManager::CreateOfflineRegionCallback // void OfflineManager::CreateOfflineRegionCallback::onError( jni::JNIEnv& env, const jni::Object& callback, std::exception_ptr error) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); } void OfflineManager::CreateOfflineRegionCallback::onCreate( jni::JNIEnv& env, const jni::Object& jFileSource, const jni::Object& callback, mbgl::OfflineRegion& region) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod)>(env, "onCreate"); callback.Call(env, method, OfflineRegion::New(env, jFileSource, std::move(region))); } // OfflineManager::MergeOfflineRegionsCallback // void OfflineManager::MergeOfflineRegionsCallback::onError( jni::JNIEnv& env, const jni::Object& callback, std::exception_ptr error) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onError"); callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); } void OfflineManager::MergeOfflineRegionsCallback::onMerge(jni::JNIEnv& env, const jni::Object& jFileSource, const jni::Object& callback, mbgl::OfflineRegions& regions) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod>)>(env, "onMerge"); std::size_t index = 0; auto jregions = jni::Array>::New(env, regions.size()); for (auto& region : regions) { jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region))); index++; } callback.Call(env, method, jregions); } void OfflineManager::putResourceWithUrl(jni::JNIEnv& env, const jni::String& url_, const jni::Array& arr, jlong modified, jlong expires, const jni::String& eTag_, jboolean mustRevalidate) { auto url = jni::Make(env, url_); auto data = std::make_shared(arr.Length(env), char()); jni::GetArrayRegion(env, *arr, 0, data->size(), reinterpret_cast(&(*data)[0])); mbgl::Resource resource(mbgl::Resource::Kind::Unknown, url); mbgl::Response response; response.data = data; response.mustRevalidate = mustRevalidate; if (eTag_) { response.etag = jni::Make(env, eTag_); } if (modified > 0) { response.modified = Timestamp(mbgl::Seconds(modified)); } if (expires > 0) { response.expires = Timestamp(mbgl::Seconds(expires)); } fileSource->put(resource, response); } } // namespace android } // namespace mbgl