From 78aa02fe8a3c335a8210068a9f6201e84e960c9e Mon Sep 17 00:00:00 2001 From: undermark5 Date: Thu, 13 Apr 2023 04:30:18 -0500 Subject: [PATCH] Fix #13130: Android respects device locale (#19419) Co-authored-by: Tulio Leao --- distribution/changelog.txt | 1 + .../main/java/io/openrct2/GameActivity.java | 65 +++++++++++++++++++ src/openrct2/platform/Platform.Android.cpp | 63 +++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 9ab4299b52..acf8da618a 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -9,6 +9,7 @@ - Improved: [#19764] Miscellaneous scenery tab now grouped next to the all-scenery tab. - Improved: [#19830] “Highlight path issues” will now hide wall elements. - Fix: [#12598] Number of holes is not set correctly when saving track designs. +- Fix: [#13130] Android always defaulting to UK locale for language, currency and temperature. - Fix: [#18895] Responding mechanic blocked at level crossing. - Fix: [#19231] Crash due to null pointer to previously deleted banner in tile copy/paste functionality - Fix: [#19296] Crash due to a race condition for parallel object loading. diff --git a/src/openrct2-android/app/src/main/java/io/openrct2/GameActivity.java b/src/openrct2-android/app/src/main/java/io/openrct2/GameActivity.java index db89ea1dd9..ebb6033e4a 100644 --- a/src/openrct2-android/app/src/main/java/io/openrct2/GameActivity.java +++ b/src/openrct2-android/app/src/main/java/io/openrct2/GameActivity.java @@ -1,15 +1,80 @@ package io.openrct2; +import android.icu.util.Currency; +import android.icu.util.LocaleData; +import android.icu.util.ULocale; +import android.os.Build; import android.view.View; import org.libsdl.app.SDLActivity; +import java.util.Locale; + public class GameActivity extends SDLActivity { public float getDefaultScale() { return getResources().getDisplayMetrics().density; } + public String getDefaultLocale(String[] supportedTags) { + Locale deviceLocale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + deviceLocale = getResources().getConfiguration().getLocales().get(0); + } else { + deviceLocale = getResources().getConfiguration().locale; + } + + for (String supportedTag : supportedTags) { + if (supportedTag.isEmpty()) continue; + String[] splits = supportedTag.split("-"); + String language = splits[0]; + String country = splits[1]; + if (deviceLocale.getLanguage().equals(language) && deviceLocale.getCountry().equals(country)) { + return supportedTag; + } + } + + Locale canadaEn = Locale.CANADA; + if (canadaEn.getLanguage().equals(deviceLocale.getLanguage()) && canadaEn.getCountry().equals(deviceLocale.getCountry())) { + return "en-US"; + } + + for (String supportedTag : supportedTags) { + if (supportedTag.isEmpty()) continue; + String[] splits = supportedTag.split("-"); + String language = splits[0]; + if (deviceLocale.getLanguage().equals(language)) { + return supportedTag; + } + } + return "en-UK"; + } + + public String getLocaleCurrency() { + Locale deviceLocale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + deviceLocale = getResources().getConfiguration().getLocales().get(0); + return Currency.getInstance(deviceLocale).getCurrencyCode(); + } else { + deviceLocale = getResources().getConfiguration().locale; + return java.util.Currency.getInstance(deviceLocale).getCurrencyCode(); + } + } + + public boolean isImperialLocaleMeasurementFormat() { + Locale deviceLocale; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + return LocaleData.getMeasurementSystem(ULocale.forLocale(getResources().getConfiguration().getLocales().get(0))) == LocaleData.MeasurementSystem.US; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + deviceLocale = getResources().getConfiguration().getLocales().get(0); + } else { + deviceLocale = getResources().getConfiguration().locale; + } + String localeCountry = deviceLocale.getCountry(); + return localeCountry.equals(Locale.US.getCountry()) || localeCountry.equals(new Locale("xx", "LR").getCountry()) || localeCountry.equals(new Locale("xx", "MM").getCountry()); + } + @Override protected String[] getLibraries() { return new String[]{ diff --git a/src/openrct2/platform/Platform.Android.cpp b/src/openrct2/platform/Platform.Android.cpp index 8dea40aca5..8ca55ac3c4 100644 --- a/src/openrct2/platform/Platform.Android.cpp +++ b/src/openrct2/platform/Platform.Android.cpp @@ -74,17 +74,74 @@ namespace Platform uint16_t GetLocaleLanguage() { - return LANGUAGE_ENGLISH_UK; + JNIEnv* env = static_cast(SDL_AndroidGetJNIEnv()); + + jobject activity = static_cast(SDL_AndroidGetActivity()); + jclass activityClass = env->GetObjectClass(activity); + jmethodID getDefaultLocale = env->GetMethodID( + activityClass, "getDefaultLocale", "([Ljava/lang/String;)Ljava/lang/String;"); + + jobjectArray jLanguageTags = env->NewObjectArray( + LANGUAGE_COUNT, env->FindClass("java/lang/String"), env->NewStringUTF("")); + + for (int32_t i = 1; i < LANGUAGE_COUNT; ++i) + { + jstring jTag = env->NewStringUTF(LanguagesDescriptors[i].locale); + env->SetObjectArrayElement(jLanguageTags, i, jTag); + } + + jstring jniString = static_cast(env->CallObjectMethod(activity, getDefaultLocale, jLanguageTags)); + + const char* jniChars = env->GetStringUTFChars(jniString, nullptr); + std::string defaultLocale = jniChars; + + env->ReleaseStringUTFChars(jniString, jniChars); + for (int32_t i = 0; i < LANGUAGE_COUNT; ++i) + { + jobject strToFree = env->GetObjectArrayElement(jLanguageTags, i); + env->DeleteLocalRef(strToFree); + } + env->DeleteLocalRef(jLanguageTags); + env->DeleteLocalRef(activity); + env->DeleteLocalRef(activityClass); + + return LanguageGetIDFromLocale(defaultLocale.c_str()); } CurrencyType GetLocaleCurrency() { - return Platform::GetCurrencyValue(NULL); + JNIEnv* env = static_cast(SDL_AndroidGetJNIEnv()); + + jobject activity = static_cast(SDL_AndroidGetActivity()); + jclass activityClass = env->GetObjectClass(activity); + jmethodID getDefaultLocale = env->GetMethodID(activityClass, "getLocaleCurrency", "()Ljava/lang/String;"); + + jstring jniString = static_cast(env->CallObjectMethod(activity, getDefaultLocale)); + + const char* jniChars = env->GetStringUTFChars(jniString, nullptr); + std::string localeCurrencyCode = jniChars; + + env->ReleaseStringUTFChars(jniString, jniChars); + env->DeleteLocalRef(activity); + env->DeleteLocalRef(activityClass); + + return Platform::GetCurrencyValue(localeCurrencyCode.c_str()); } MeasurementFormat GetLocaleMeasurementFormat() { - return MeasurementFormat::Metric; + JNIEnv* env = static_cast(SDL_AndroidGetJNIEnv()); + + jobject activity = static_cast(SDL_AndroidGetActivity()); + jclass activityClass = env->GetObjectClass(activity); + jmethodID getIsImperialLocaleMeasurementFormat = env->GetMethodID( + activityClass, "isImperialLocaleMeasurementFormat", "()Z"); + + jboolean isImperial = env->CallBooleanMethod(activity, getIsImperialLocaleMeasurementFormat); + + env->DeleteLocalRef(activity); + env->DeleteLocalRef(activityClass); + return isImperial == JNI_TRUE ? MeasurementFormat::Imperial : MeasurementFormat::Metric; } std::string GetSteamPath()