Move JNI bindings code from mapbox-gl-native to this repo. (#301)
This commit is contained in:
192
MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp
Normal file
192
MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp
Normal 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
|
||||
62
MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp
Normal file
62
MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp
Normal 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
|
||||
80
MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp
Normal file
80
MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp
Normal 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
|
||||
29
MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp
Normal file
29
MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp
Normal 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
|
||||
Reference in New Issue
Block a user