#include "offline_region.hpp" #include #include #include #include "offline_region_definition.hpp" #include "offline_region_error.hpp" #include "offline_region_status.hpp" #include "../attach_env.hpp" namespace mbgl { namespace android { // OfflineRegion // OfflineRegion::OfflineRegion(jni::JNIEnv& env, jni::jlong offlineRegionPtr, const jni::Object& jFileSource) : region(reinterpret_cast(offlineRegionPtr)), fileSource(std::static_pointer_cast( std::shared_ptr(mbgl::FileSourceManager::get()->getFileSource( mbgl::FileSourceType::Database, FileSource::getSharedResourceOptions(env, jFileSource))))) { if (!fileSource) { ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Offline functionality is disabled."); } } OfflineRegion::~OfflineRegion() {} void OfflineRegion::setOfflineRegionObserver(jni::JNIEnv& env_, const jni::Object& callback) { // Define the observer class Observer : public mbgl::OfflineRegionObserver { public: Observer(jni::Global, jni::EnvAttachingDeleter> callback_) : callback(std::move(callback_)) { } void statusChanged(mbgl::OfflineRegionStatus status) override { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*env); static auto method = javaClass.GetMethod)>(*env, "onStatusChanged"); callback.Call(*env, method, OfflineRegionStatus::New(*env, status)); } void responseError(mbgl::Response::Error error) override { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*env); static auto method = javaClass.GetMethod)>(*env, "onError"); callback.Call(*env, method, OfflineRegionError::New(*env, error)); } void mapboxTileCountLimitExceeded(uint64_t limit) override { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); static auto& javaClass = jni::Class::Singleton(*env); static auto method = javaClass.GetMethod(*env, "mapboxTileCountLimitExceeded"); callback.Call(*env, method, jlong(limit)); } jni::Global, jni::EnvAttachingDeleter> callback; }; // Set the observer fileSource->setOfflineRegionObserver(*region, std::make_unique(jni::NewGlobal(env_, callback))); } void OfflineRegion::setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint jState) { // State mbgl::OfflineRegionDownloadState state; switch (jState) { case 0: state = mbgl::OfflineRegionDownloadState::Inactive; break; case 1: state = mbgl::OfflineRegionDownloadState::Active; break; default: mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active)."); return; } fileSource->setOfflineRegionDownloadState(*region, state); } void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->getOfflineRegionStatus(*region, [ //Ensure the object is not gc'd in the meanwhile callback = std::make_shared(std::move(globalCallback)) ](mbgl::expected status) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (status) { OfflineRegionStatusCallback::onStatus(*env, *callback, std::move(*status)); } else { OfflineRegionStatusCallback::onError(*env, *callback, status.error()); } }); } void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->deleteOfflineRegion(*region, [ // Ensure the object is not gc'd in the meanwhile callback = std::make_shared( std::move(globalCallback))](std::exception_ptr error) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (error) { OfflineRegionDeleteCallback::onError(*env, *callback, error); } else { OfflineRegionDeleteCallback::onDelete(*env, *callback); } }); } void OfflineRegion::invalidateOfflineRegion(jni::JNIEnv& env_, const jni::Object& callback_) { auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->invalidateOfflineRegion(*region, [ // Ensure the object is not gc'd in the meanwhile callback = std::make_shared( std::move(globalCallback))](std::exception_ptr error) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (error) { OfflineRegionInvalidateCallback::onError(*env, *callback, error); } else { OfflineRegionInvalidateCallback::onInvalidate(*env, *callback); } }); } void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, const jni::Array& jMetadata, const jni::Object& callback_) { auto metadata = OfflineRegion::metadata(env_, jMetadata); auto globalCallback = jni::NewGlobal(env_, callback_); fileSource->updateOfflineMetadata(region->getID(), metadata, [ //Ensure the object is not gc'd in the meanwhile callback = std::make_shared(std::move(globalCallback)) ](mbgl::expected data) mutable { // Reattach, the callback comes from a different thread android::UniqueEnv env = android::AttachEnv(); if (data) { OfflineRegionUpdateMetadataCallback::onUpdate(*env, *callback, std::move(*data)); } else { OfflineRegionUpdateMetadataCallback::onError(*env, *callback, data.error()); } }); } jni::Local> OfflineRegion::New(jni::JNIEnv& env, const jni::Object& jFileSource, mbgl::OfflineRegion region) { // Definition auto definition = region.getDefinition().match( [&](const mbgl::OfflineTilePyramidRegionDefinition def) { return OfflineTilePyramidRegionDefinition::New(env, def); }, [&](const mbgl::OfflineGeometryRegionDefinition def) { return OfflineGeometryRegionDefinition::New(env, def); }); // Create region java object static auto& javaClass = jni::Class::Singleton(env); static auto constructor = javaClass.GetConstructor, jni::jlong, jni::Object, jni::Array>(env); return javaClass.New(env, constructor, reinterpret_cast(new mbgl::OfflineRegion(std::move(region))), //Copy a region to the heap jFileSource, jni::jlong(region.getID()), definition, OfflineRegion::metadata(env, region.getMetadata())); } jni::Local> OfflineRegion::metadata(jni::JNIEnv& env, mbgl::OfflineRegionMetadata metadata_) { std::vector convertedMetadata(metadata_.begin(), metadata_.end()); std::size_t length = static_cast(convertedMetadata.size()); auto metadata = jni::Array::New(env, length); metadata.SetRegion>(env, 0, convertedMetadata); return metadata; } mbgl::OfflineRegionMetadata OfflineRegion::metadata(jni::JNIEnv& env, const jni::Array& metadata_) { std::size_t length = metadata_.Length(env); auto metadata_tmp = std::vector(); metadata_tmp.resize(length); metadata_.GetRegion>(env, 0, metadata_tmp); auto metadata = std::vector(metadata_tmp.begin(), metadata_tmp.end()); return metadata; } void OfflineRegion::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(&OfflineRegion::setOfflineRegionObserver, "setOfflineRegionObserver"), METHOD(&OfflineRegion::setOfflineRegionDownloadState, "setOfflineRegionDownloadState"), METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"), METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"), METHOD(&OfflineRegion::invalidateOfflineRegion, "invalidateOfflineRegion"), METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata")); } // OfflineRegionObserver // // OfflineRegionStatusCallback // void OfflineRegion::OfflineRegionStatusCallback::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 OfflineRegion::OfflineRegionStatusCallback::onStatus(jni::JNIEnv& env, const jni::Object& callback, mbgl::optional status) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod)>(env, "onStatus"); callback.Call(env, method, OfflineRegionStatus::New(env, std::move(*status))); } // OfflineRegionDeleteCallback // void OfflineRegion::OfflineRegionDeleteCallback::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 OfflineRegion::OfflineRegionDeleteCallback::onDelete(jni::JNIEnv& env, const jni::Object& callback) { // Trigger callback static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onDelete"); callback.Call(env, method); } // OfflineRegionUpdateMetadataCallback // void OfflineRegion::OfflineRegionUpdateMetadataCallback::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 OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& env, const jni::Object& callback, mbgl::optional metadata) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod)>(env, "onUpdate"); callback.Call(env, method, OfflineRegion::metadata(env, std::move(*metadata))); } // OfflineRegionInvalidateCallback // void OfflineRegion::OfflineRegionInvalidateCallback::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 OfflineRegion::OfflineRegionInvalidateCallback::onInvalidate(jni::JNIEnv& env, const jni::Object& callback) { static auto& javaClass = jni::Class::Singleton(env); static auto method = javaClass.GetMethod(env, "onInvalidate"); callback.Call(env, method); } } // namespace android } // namespace mbgl