Move JNI bindings code from mapbox-gl-native to this repo. (#301)

This commit is contained in:
Igor Tarasov
2020-04-02 19:45:56 +03:00
committed by GitHub
parent 620bda356c
commit daecbed823
178 changed files with 14422 additions and 2 deletions

View File

@@ -0,0 +1,192 @@
#include <mbgl/i18n/collator.hpp>
#include <mbgl/text/language_tag.hpp>
#include <jni/jni.hpp>
#include "attach_env.hpp"
#include "collator_jni.hpp"
namespace mbgl {
namespace android {
void Collator::registerNative(jni::JNIEnv& env) {
jni::Class<Collator>::Singleton(env);
}
jni::Local<jni::Object<Collator>> Collator::getInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Collator> (jni::Object<Locale>)>(env, "getInstance");
return javaClass.Call(env, method, locale);
}
void Collator::setStrength(jni::JNIEnv& env, const jni::Object<Collator>& collator, jni::jint strength) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetMethod<void (jni::jint)>(env, "setStrength");
collator.Call(env, method, strength);
}
jni::jint Collator::compare(jni::JNIEnv& env, const jni::Object<Collator>& collator, const jni::String& lhs, const jni::String& rhs) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetMethod<jni::jint (jni::String, jni::String)>(env, "compare");
return collator.Call(env, method, lhs, rhs);
}
void StringUtils::registerNative(jni::JNIEnv& env) {
jni::Class<StringUtils>::Singleton(env);
}
jni::Local<jni::String> StringUtils::unaccent(jni::JNIEnv& env, const jni::String& value) {
static auto& javaClass = jni::Class<StringUtils>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::String (jni::String)>(env, "unaccent");
return javaClass.Call(env, method, value);
}
void Locale::registerNative(jni::JNIEnv& env) {
jni::Class<Locale>::Singleton(env);
}
/*
We would prefer to use for/toLanguageTag, but they're only available in API level 21+
jni::Object<Locale> Locale::forLanguageTag(jni::JNIEnv& env, jni::String languageTag) {
using Signature = jni::Object<Locale>(jni::String);
auto method = javaClass.GetStaticMethod<Signature>(env, "forLanguageTag");
return javaClass.Call(env, method, languageTag);
}
jni::String Locale::toLanguageTag(jni::JNIEnv& env, jni::Object<Locale> locale) {
using Signature = jni::String();
auto static method = javaClass.GetMethod<Signature>(env, "toLanguageTag");
return locale.Call(env, method);
}
*/
jni::Local<jni::String> Locale::getLanguage(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String ()>(env, "getLanguage");
return locale.Call(env, method);
}
jni::Local<jni::String> Locale::getCountry(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String ()>(env, "getCountry");
return locale.Call(env, method);
}
jni::Local<jni::Object<Locale>> Locale::getDefault(jni::JNIEnv& env) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Locale> ()>(env, "getDefault");
return javaClass.Call(env, method);
}
jni::Local<jni::Object<Locale>> Locale::New(jni::JNIEnv& env, const jni::String& language) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jni::String>(env);
return javaClass.New(env, constructor, language);
}
jni::Local<jni::Object<Locale>> Locale::New(jni::JNIEnv& env, const jni::String& language, const jni::String& region) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jni::String, jni::String>(env);
return javaClass.New(env, constructor, language, region);
}
} // namespace android
namespace platform {
class Collator::Impl {
public:
Impl(bool caseSensitive_, bool diacriticSensitive_, optional<std::string> locale_)
: caseSensitive(caseSensitive_)
, diacriticSensitive(diacriticSensitive_)
, env(android::AttachEnv())
{
LanguageTag languageTag = locale_ ? LanguageTag::fromBCP47(*locale_) : LanguageTag();
if (!languageTag.language) {
locale = jni::NewGlobal(*env,
android::Locale::getDefault(*env));
} else if (!languageTag.region) {
locale = jni::NewGlobal(*env,
android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language)));
} else {
locale = jni::NewGlobal(*env,
android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language),
jni::Make<jni::String>(*env, *languageTag.region)));
}
collator = jni::NewGlobal(*env, android::Collator::getInstance(*env, locale));
if (!diacriticSensitive && !caseSensitive) {
android::Collator::setStrength(*env, collator, 0 /*PRIMARY*/);
} else if (diacriticSensitive && !caseSensitive) {
android::Collator::setStrength(*env, collator, 1 /*SECONDARY*/);
} else if (caseSensitive) {
// If we're case-sensitive and diacritic-sensitive, we use a case-sensitive collator
// and a fallback implementation of diacritic-insensitivity.
android::Collator::setStrength(*env, collator, 2 /*TERTIARY*/);
}
}
bool operator==(const Impl& other) const {
return caseSensitive == other.caseSensitive &&
diacriticSensitive == other.diacriticSensitive &&
resolvedLocale() == other.resolvedLocale();
}
int compare(const std::string& lhs, const std::string& rhs) const {
bool useUnaccent = !diacriticSensitive && caseSensitive;
// java.text.Collator doesn't support a diacritic-insensitive/case-sensitive collation
// order, so we have to compromise here. We use Android's case-sensitive Collator
// against strings that have been "unaccented" using non-locale-aware nunicode logic.
// Because of the difference in locale-awareness, this means turning on case-sensitivity
// can _potentially_ change compare results for strings that don't actually have any case
// differences.
jni::Local<jni::String> jlhs = useUnaccent
? android::StringUtils::unaccent(*env, jni::Make<jni::String>(*env, lhs))
: jni::Make<jni::String>(*env, lhs);
jni::Local<jni::String> jrhs = useUnaccent
? android::StringUtils::unaccent(*env, jni::Make<jni::String>(*env, rhs))
: jni::Make<jni::String>(*env, rhs);
jni::jint result = android::Collator::compare(*env, collator, jlhs, jrhs);
return result;
}
std::string resolvedLocale() const {
std::string language = jni::Make<std::string>(*env, android::Locale::getLanguage(*env, locale));
std::string region = jni::Make<std::string>(*env, android::Locale::getCountry(*env, locale));
optional<std::string> resultLanguage;
if (!language.empty()) resultLanguage = language;
optional<std::string> resultRegion;
if (!region.empty()) resultRegion = region;
return LanguageTag(resultLanguage, {}, resultRegion).toBCP47();
}
private:
bool caseSensitive;
bool diacriticSensitive;
android::UniqueEnv env;
jni::Global<jni::Object<android::Collator>> collator;
jni::Global<jni::Object<android::Locale>> locale;
};
Collator::Collator(bool caseSensitive, bool diacriticSensitive, const optional<std::string>& locale_)
: impl(std::make_shared<Impl>(caseSensitive, diacriticSensitive, locale_)) {}
bool Collator::operator==(const Collator& other) const {
return *impl == *(other.impl);
}
int Collator::compare(const std::string& lhs, const std::string& rhs) const {
return impl->compare(lhs, rhs);
}
std::string Collator::resolvedLocale() const {
return impl->resolvedLocale();
}
} // namespace platform
} // namespace mbgl

View File

@@ -0,0 +1,62 @@
#pragma once
#include <jni/jni.hpp>
/*
android::Collator and android::Locale are
the JNI wrappers of
java/text/Collator and java/util/Locale
mbgl::Collator is the portable interface
Both implementations are in collator.cpp
*/
namespace mbgl {
namespace android {
class Locale {
public:
static constexpr auto Name() { return "java/util/Locale"; };
/* Requires API level 21+ in order to support script/variant
static jni::Object<Locale> forLanguageTag(jni::JNIEnv&, jni::String);
static jni::String toLanguageTag(jni::JNIEnv&, jni::Object<Locale>);
*/
static jni::Local<jni::Object<Locale>> getDefault(jni::JNIEnv&);
static jni::Local<jni::String> getLanguage(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::String> getCountry(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::Object<Locale>> New(jni::JNIEnv&, const jni::String&);
static jni::Local<jni::Object<Locale>> New(jni::JNIEnv&, const jni::String&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
class Collator {
public:
static constexpr auto Name() { return "java/text/Collator"; };
static jni::Local<jni::Object<Collator>> getInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static void setStrength(jni::JNIEnv&, const jni::Object<Collator>&, jni::jint);
static jni::jint compare(jni::JNIEnv&, const jni::Object<Collator>&, const jni::String&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
class StringUtils {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/utils/StringUtils"; };
static jni::Local<jni::String> unaccent(jni::JNIEnv&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,80 @@
#include <mbgl/i18n/number_format.hpp>
#include <mbgl/text/language_tag.hpp>
#include <jni/jni.hpp>
#include "attach_env.hpp"
#include "number_format_jni.hpp"
namespace mbgl {
namespace android {
void NumberFormat::registerNative(jni::JNIEnv& env) {
jni::Class<NumberFormat>::Singleton(env);
}
jni::Local<jni::Object<NumberFormat>> NumberFormat::getInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat> (jni::Object<Locale>)>(env, "getInstance");
return javaClass.Call(env, method, locale);
}
jni::Local<jni::Object<NumberFormat>> NumberFormat::getCurrencyInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat> (jni::Object<Locale>)>(env, "getCurrencyInstance");
return javaClass.Call(env, method, locale);
}
jni::Local<jni::String> NumberFormat::format(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jdouble number) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String (jni::jdouble)>(env, "format");
return nf.Call(env, method, number);
}
void NumberFormat::setMinimumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<void (jni::jint)>(env, "setMinimumFractionDigits");
return nf.Call(env, method, value);
}
void NumberFormat::setMaximumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<void (jni::jint)>(env, "setMaximumFractionDigits");
return nf.Call(env, method, value);
}
} // namespace android
namespace platform {
std::string formatNumber(double number, const std::string& localeId, const std::string& currency,
uint8_t minFractionDigits, uint8_t maxFractionDigits) {
auto env{ android::AttachEnv() };
jni::Global<jni::Object<android::Locale>> locale;
LanguageTag languageTag = !localeId.empty() ? LanguageTag::fromBCP47(localeId) : LanguageTag();
if (!languageTag.language) {
locale = jni::NewGlobal(*env, android::Locale::getDefault(*env));
} else if (!languageTag.region) {
locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language)));
} else {
locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language),
jni::Make<jni::String>(*env, *languageTag.region)));
}
jni::Global<jni::Object<android::NumberFormat>> formatter;
if (currency.empty()) {
formatter = jni::NewGlobal(*env, android::NumberFormat::getInstance(*env, locale));
android::NumberFormat::setMinimumFractionDigits(*env, formatter, static_cast<jni::jint>(minFractionDigits));
android::NumberFormat::setMaximumFractionDigits(*env, formatter, static_cast<jni::jint>(maxFractionDigits));
} else {
formatter = jni::NewGlobal(*env, android::NumberFormat::getCurrencyInstance(*env, locale));
}
auto result = android::NumberFormat::format(*env, formatter, static_cast<jni::jdouble>(number));
return jni::Make<std::string>(*env, result);
}
} // namespace platform
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#pragma once
#include <jni/jni.hpp>
#include "collator_jni.hpp"
/*
android::NumberFormat is the JNI wrapper
of java/text/NumberFormat.
*/
namespace mbgl {
namespace android {
class NumberFormat {
public:
static constexpr auto Name() { return "java/text/NumberFormat"; };
static jni::Local<jni::Object<NumberFormat>> getInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::Object<NumberFormat>> getCurrencyInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::String> format(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jdouble);
static void setMinimumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
static void setMaximumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl