Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): add support for Android's API 33 Per-app languages #14120

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollModule;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.kroll.common.Log;
Expand All @@ -21,8 +23,12 @@
import org.appcelerator.titanium.util.TiPlatformHelper;
import org.appcelerator.titanium.util.TiRHelper;

import android.os.Build;
import android.telephony.PhoneNumberUtils;

import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.os.LocaleListCompat;

@Kroll.module
public class LocaleModule extends KrollModule
{
Expand All @@ -33,6 +39,62 @@ public LocaleModule()
super("Locale");
}

@Kroll.setProperty
public void setApplicationLocales(String locales)
{
if (Build.VERSION.SDK_INT < 33) {
Log.w(TAG, "This property is only supported on Android API level 33 and above.");
return;
}
LocaleListCompat appLocale = LocaleListCompat.forLanguageTags(locales);
AppCompatDelegate.setApplicationLocales(appLocale);
}

@Kroll.getProperty
public KrollDict[] getApplicationLocales()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is very uncommon to have different types for the getter and setter. And there is Ti.Locale.setLanguage already. So I would propose:

  • Use the new Android 33+ API in Ti.Locale.setLanguage(language)
  • Expose the getter as a new Titanium API (if there isn't one already to get language details - if so, update that one)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this PR is with no use so I'll close it

{
if (Build.VERSION.SDK_INT < 33) {
Log.w(TAG, "This property is only supported on Android API level 33 and above.");
return new KrollDict[0];
}
LocaleListCompat localeListCompat = AppCompatDelegate.getApplicationLocales();
int size = localeListCompat.size();
KrollDict[] locales = new KrollDict[size];
for (int i = 0; i < size; i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop looks a bit "oldskool". Things to change:

  • Use a more modern loop technique, e.g. this one and append it a to an array instead of maintaining an index
  • Do not add all the logic in the null check. Instead, check for locale == null and continue early

Locale locale = localeListCompat.get(i);
if (locale != null) {
KrollDict localeObj = new KrollDict();
localeObj.put("country", locale.getCountry());
localeObj.put("iso3Country", locale.getISO3Country());
localeObj.put("displayCountry", locale.getDisplayCountry());
localeObj.put("language", locale.getLanguage());
localeObj.put("iso3Language", locale.getISO3Language());
localeObj.put("displayLanguage", locale.getDisplayLanguage());
localeObj.put("variant", locale.getVariant());
localeObj.put("displayVariant", locale.getDisplayVariant());
localeObj.put("script", locale.getScript());
localeObj.put("displayScript", locale.getDisplayScript());
localeObj.put("displayName", locale.getDisplayName());
localeObj.put("languageTag", locale.toLanguageTag());
Character[] extensionKeys = new Character[locale.getExtensionKeys().size()];
String[] extensions = new String[locale.getExtensionKeys().size()];
Iterator<Character> extensionKeysSize = locale.getExtensionKeys().iterator();
int l = 0;
while (extensionKeysSize.hasNext()) {
extensionKeys[l] = extensionKeysSize.next();
extensions[l] = locale.getExtension(extensionKeys[l]);
l++;
}
localeObj.put("extension_keys", extensionKeys);
localeObj.put("extensions", extensions);
locales[i] = localeObj;
} else {
locales[i] = null;
}
}
return locales;
}

@Kroll.getProperty
public String getCurrentLanguage()
{
Expand Down Expand Up @@ -79,10 +141,9 @@ public String getLocaleCurrencySymbol(String localeString)

/**
* Undocumented method used to implement the JavaScript Intl.getCanonicalLocales() static method.
* @param locales
* Can be a string or array of strings providing locale IDs to convert to canonical locale IDs. Can be null.
* @return
* Returns the given locale string IDs converted to "canonical" string IDs. Duplicate locales are removed.
*
* @param locales Can be a string or array of strings providing locale IDs to convert to canonical locale IDs. Can be null.
* @return Returns the given locale string IDs converted to "canonical" string IDs. Duplicate locales are removed.
* Returns an empty array if given locales are invalid/unsupported or if given a null locales argument.
*/
@Kroll.method
Expand All @@ -104,10 +165,10 @@ public String[] getCanonicalLocales(@Kroll.argument(optional = true) Object loca

/**
* Undocumented method used to implement the JavaScript Intl.Collator.supportedLocalesOf() static method.
*
* @param locales Can be a string or array of strings providing the locale IDs to search for. Can be null.
* @param options The Intl.Collator.supportedLocalesOf() argument. Currently ignored.
* @return
* Returns a subset of locale IDs from the given argument that are supported by the system.
* @return Returns a subset of locale IDs from the given argument that are supported by the system.
* Returns an empty array if none of the locales are supported or if given a null locales argument.
*/
@Kroll.method
Expand All @@ -120,10 +181,10 @@ public String[] getSupportedCollatorLocales(Object locales, @Kroll.argument(opti

/**
* Undocumented method used to implement the JavaScript Intl.DateTimeFormat.supportedLocalesOf() static method.
*
* @param locales Can be a string or array of strings providing the locale IDs to search for. Can be null.
* @param options The Intl.DateTimeFormat.supportedLocalesOf() argument. Currently ignored.
* @return
* Returns a subset of locale IDs from the given argument that are supported by the system.
* @return Returns a subset of locale IDs from the given argument that are supported by the system.
* Returns an empty array if none of the locales are supported or if given a null locales argument.
*/
@Kroll.method
Expand All @@ -136,10 +197,10 @@ public String[] getSupportedDateTimeFormatLocales(Object locales, @Kroll.argumen

/**
* Undocumented method used to implement the JavaScript Intl.NumberFormat.supportedLocalesOf() static method.
*
* @param locales Can be a string or array of strings providing the locale IDs to search for. Can be null.
* @param options The Intl.NumberFormat.supportedLocalesOf() argument. Currently ignored.
* @return
* Returns a subset of locale IDs from the given argument that are supported by the system.
* @return Returns a subset of locale IDs from the given argument that are supported by the system.
* Returns an empty array if none of the locales are supported or if given a null locales argument.
*/
@Kroll.method
Expand Down
2 changes: 1 addition & 1 deletion android/templates/build/ti.constants.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

project.ext {
tiNdkVersion = '26.2.11394342'
tiAndroidXAppCompatLibVersion = '1.4.1'
tiAndroidXAppCompatLibVersion = '1.6.0'
tiAndroidXCoreLibVersion = '1.9.0'
tiAndroidXFragmentLibVersion = '1.5.7'
tiMaterialLibVersion = '1.6.1'
Expand Down
11 changes: 11 additions & 0 deletions apidoc/Titanium/Locale/Locale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,14 @@ properties:
sections of wikipedia for reference.
type: String
permission: read-only

- name: applicationLocales
summary: Update or retrieve Android's Per-app languages.
platforms: [android]
description: |
This property holds the values of Android's Per-app languages which is an array of defined Per-app languages,
if no languages defined, an empty array will be returned. You can set the value of it like this `en-US,en-GB,ar-SA`.
This property requires Android API level 33 and above.
See the [Android's Per-app languages](https://developer.android.com/guide/topics/resources/app-languages).
type: Array
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the array typings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please explain

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the newer comments to split the API. But in general, you just have an array as the return value, but really it's Array<LocaleDetails> to document all properties. This should not just refer to the Android docs, as there may be newer properties in the future that are not exposed in Titanium, yet.

since: "12.6.0"
Loading