OSDN Git Service

For locale negotiation, assume English is always supported
authorRoozbeh Pournader <roozbeh@google.com>
Wed, 16 Dec 2015 07:56:11 +0000 (23:56 -0800)
committerRoozbeh Pournader <roozbeh@google.com>
Tue, 5 Jan 2016 22:52:38 +0000 (14:52 -0800)
In practice, a lot of apps provide English resources as their default
resources, so assume that English is always supported.

With this change, users can safely set secondary locales after
English in their preferred locale lists. Such settings would help
the apps (and parts of the system that are aware of multiple locales)
consider the user's familiarity with the secondary locale or the
user's preference for glyphs forms in the secondary locale.  This
change makes sure that no app unintentionally switches to such
secondary locales.

Note that this doesn't break from the tradition of previous releases.
Traditionally, if the user had set the system locale to English, they
would get the default resources when the app didn't explicitly
provide English resources. This will continue to be the case where
the user's locale list has English in it before another supported
language. (English will be the first locale in most cases.)

Bug: 26192817
Change-Id: I2c1a003760299157786c1b3f9fb116ce18db3b8c

core/java/android/content/res/Resources.java
core/java/android/util/LocaleList.java
core/tests/coretests/Android.mk
core/tests/coretests/res/values-fa/strings.xml [new file with mode: 0644]
core/tests/coretests/res/values/strings.xml
core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java [new file with mode: 0644]

index c460746..b01633e 100644 (file)
@@ -1978,7 +1978,7 @@ public class Resources {
                     || (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) {
                 if (locales.size() == 1) {
                     // This is an optimization to avoid the JNI call(s) when the result of
-                    // getFirstMatch() does not depend on the supported locales.
+                    // getFirstMatchWithEnglishSupported() does not depend on the supported locales.
                     mResolvedLocale = locales.getPrimary();
                 } else {
                     String[] supportedLocales = mAssets.getNonSystemLocales();
@@ -1989,7 +1989,7 @@ public class Resources {
                         // their own.
                         supportedLocales = mAssets.getLocales();
                     }
-                    mResolvedLocale = locales.getFirstMatch(supportedLocales);
+                    mResolvedLocale = locales.getFirstMatchWithEnglishSupported(supportedLocales);
                 }
             }
             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
index 3d8e330..f22cde0 100644 (file)
@@ -246,14 +246,9 @@ public final class LocaleList implements Parcelable {
         return supportedScr.equals(desiredScr) ? 1 : 0;
     }
 
-    /**
-     * Returns the first match in the locale list given an unordered array of supported locales
-     * in BCP47 format.
-     *
-     * If the locale list is empty, null would be returned.
-     */
-    @Nullable
-    public Locale getFirstMatch(String[] supportedLocales) {
+    private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn");
+
+    private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) {
         if (mList.length == 1) {  // just one locale, perhaps the most common scenario
             return mList[0];
         }
@@ -261,8 +256,16 @@ public final class LocaleList implements Parcelable {
             return null;
         }
         int bestIndex = Integer.MAX_VALUE;
-        for (String tag : supportedLocales) {
-            final Locale supportedLocale = Locale.forLanguageTag(tag);
+        final int numSupportedLocales =
+                supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0);
+        for (int i = 0; i < numSupportedLocales; i++) {
+            final Locale supportedLocale;
+            if (assumeEnglishIsSupported) {
+                // Try English first, so we can return early if it's in the LocaleList
+                supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]);
+            } else {
+                supportedLocale = Locale.forLanguageTag(supportedLocales[i]);
+            }
             // We expect the average length of locale lists used for locale resolution to be
             // smaller than three, so it's OK to do this as an O(mn) algorithm.
             for (int idx = 0; idx < mList.length; idx++) {
@@ -284,6 +287,26 @@ public final class LocaleList implements Parcelable {
     }
 
     /**
+     * Returns the first match in the locale list given an unordered array of supported locales
+     * in BCP47 format.
+     *
+     * If the locale list is empty, null would be returned.
+     */
+    @Nullable
+    public Locale getFirstMatch(String[] supportedLocales) {
+        return computeFirstMatch(supportedLocales, false /* assume English is not supported */);
+    }
+
+    /**
+     * Same as getFirstMatch(), but with English assumed to be supported, even if it's not.
+     * {@hide}
+     */
+    @Nullable
+    public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) {
+        return computeFirstMatch(supportedLocales, true /* assume English is supported */);
+    }
+
+    /**
      * Returns true if the array of locale tags only contains empty locales and pseudolocales.
      * Assumes that there is no repetition in the input.
      * {@hide}
index 7cd25af..ee8921e 100644 (file)
@@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
        $(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_AAPT_FLAGS = -0 dat -0 gld
+LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
 LOCAL_STATIC_JAVA_LIBRARIES := \
     core-tests-support \
     android-common \
diff --git a/core/tests/coretests/res/values-fa/strings.xml b/core/tests/coretests/res/values-fa/strings.xml
new file mode 100644 (file)
index 0000000..c83f5f1
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="dummy_string">رشتهٔ الکی</string>
+</resources>
index 04b0478..ef915bb 100644 (file)
     <!-- RestrictionsManagerTest -->
     <string name="restrictionManager_title">Title</string>
     <string name="restrictionManager_desc">Description</string>
+
+    <!-- ResourcesLocaleResolutionTest -->
+    <string name="dummy_string">dummy string</string>
 </resources>
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleResolutionTest.java
new file mode 100644 (file)
index 0000000..55c0031
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.content.res;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+
+import java.util.Arrays;
+import java.util.Locale;
+
+public class ResourcesLocaleResolutionTest extends AndroidTestCase {
+    @SmallTest
+    public void testGetResolvedLocale_englishIsAlwaysConsideredSupported() {
+        // First make sure English has no explicit assets other than the default assets
+        final AssetManager assets = getContext().getAssets();
+        final String supportedLocales[] = assets.getNonSystemLocales();
+        for (String languageTag : supportedLocales) {
+            if ("en-XA".equals(languageTag)) {
+                continue;
+            }
+            assertFalse(
+                    "supported locales: " + Arrays.toString(supportedLocales),
+                    "en".equals(Locale.forLanguageTag(languageTag).getLanguage()));
+        }
+
+        final DisplayMetrics dm = new DisplayMetrics();
+        dm.setToDefaults();
+        final Configuration cfg = new Configuration();
+        cfg.setToDefaults();
+        // Avestan and English have no assets, but Persian does.
+        cfg.setLocales(LocaleList.forLanguageTags("ae,en,fa"));
+        Resources res = new Resources(assets, dm, cfg);
+        // We should get English, because it is always considered supported.
+        assertEquals("en", res.getResolvedLocale().toLanguageTag());
+    }
+}
+