Merge "Support properly setting and getting LocaleLists"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a514b63749
@@ -39109,7 +39109,9 @@ package android.util {
|
||||
method public static android.util.LocaleList getEmptyLocaleList();
|
||||
method public java.util.Locale getFirstMatch(java.lang.String[]);
|
||||
method public java.util.Locale getPrimary();
|
||||
method public int indexOf(java.util.Locale);
|
||||
method public boolean isEmpty();
|
||||
method public static void setDefault(android.util.LocaleList);
|
||||
method public int size();
|
||||
method public java.lang.String toLanguageTags();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
|
||||
@@ -41457,7 +41457,9 @@ package android.util {
|
||||
method public static android.util.LocaleList getEmptyLocaleList();
|
||||
method public java.util.Locale getFirstMatch(java.lang.String[]);
|
||||
method public java.util.Locale getPrimary();
|
||||
method public int indexOf(java.util.Locale);
|
||||
method public boolean isEmpty();
|
||||
method public static void setDefault(android.util.LocaleList);
|
||||
method public int size();
|
||||
method public java.lang.String toLanguageTags();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
|
||||
@@ -39125,7 +39125,9 @@ package android.util {
|
||||
method public static android.util.LocaleList getEmptyLocaleList();
|
||||
method public java.util.Locale getFirstMatch(java.lang.String[]);
|
||||
method public java.util.Locale getPrimary();
|
||||
method public int indexOf(java.util.Locale);
|
||||
method public boolean isEmpty();
|
||||
method public static void setDefault(android.util.LocaleList);
|
||||
method public int size();
|
||||
method public java.lang.String toLanguageTags();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.util;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.Size;
|
||||
@@ -56,10 +57,21 @@ public final class LocaleList implements Parcelable {
|
||||
return mList.length == 0;
|
||||
}
|
||||
|
||||
@IntRange(from=0)
|
||||
public int size() {
|
||||
return mList.length;
|
||||
}
|
||||
|
||||
@IntRange(from=-1)
|
||||
public int indexOf(Locale locale) {
|
||||
for (int i = 0; i < mList.length; i++) {
|
||||
if (mList[i].equals(locale)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == this)
|
||||
@@ -69,7 +81,7 @@ public final class LocaleList implements Parcelable {
|
||||
final Locale[] otherList = ((LocaleList) other).mList;
|
||||
if (mList.length != otherList.length)
|
||||
return false;
|
||||
for (int i = 0; i < mList.length; ++i) {
|
||||
for (int i = 0; i < mList.length; i++) {
|
||||
if (!mList[i].equals(otherList[i]))
|
||||
return false;
|
||||
}
|
||||
@@ -79,7 +91,7 @@ public final class LocaleList implements Parcelable {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
for (int i = 0; i < mList.length; ++i) {
|
||||
for (int i = 0; i < mList.length; i++) {
|
||||
result = 31 * result + mList[i].hashCode();
|
||||
}
|
||||
return result;
|
||||
@@ -89,7 +101,7 @@ public final class LocaleList implements Parcelable {
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
for (int i = 0; i < mList.length; ++i) {
|
||||
for (int i = 0; i < mList.length; i++) {
|
||||
sb.append(mList[i]);
|
||||
if (i < mList.length - 1) {
|
||||
sb.append(',');
|
||||
@@ -150,12 +162,12 @@ public final class LocaleList implements Parcelable {
|
||||
final Locale[] localeList = new Locale[list.length];
|
||||
final HashSet<Locale> seenLocales = new HashSet<Locale>();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < list.length; ++i) {
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
final Locale l = list[i];
|
||||
if (l == null) {
|
||||
throw new NullPointerException();
|
||||
throw new NullPointerException("list[" + i + "] is null");
|
||||
} else if (seenLocales.contains(l)) {
|
||||
throw new IllegalArgumentException();
|
||||
throw new IllegalArgumentException("list[" + i + "] is a repetition");
|
||||
} else {
|
||||
final Locale localeClone = (Locale) l.clone();
|
||||
localeList[i] = localeClone;
|
||||
@@ -171,6 +183,55 @@ public final class LocaleList implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a locale list, with the topLocale moved to the front if it already is
|
||||
* in otherLocales, or added to the front if it isn't.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public LocaleList(@NonNull Locale topLocale, LocaleList otherLocales) {
|
||||
if (topLocale == null) {
|
||||
throw new NullPointerException("topLocale is null");
|
||||
}
|
||||
|
||||
final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length;
|
||||
int topLocaleIndex = -1;
|
||||
for (int i = 0; i < inputLength; i++) {
|
||||
if (topLocale.equals(otherLocales.mList[i])) {
|
||||
topLocaleIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0);
|
||||
final Locale[] localeList = new Locale[outputLength];
|
||||
localeList[0] = (Locale) topLocale.clone();
|
||||
if (topLocaleIndex == -1) {
|
||||
// topLocale was not in otherLocales
|
||||
for (int i = 0; i < inputLength; i++) {
|
||||
localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < topLocaleIndex; i++) {
|
||||
localeList[i + 1] = (Locale) otherLocales.mList[i].clone();
|
||||
}
|
||||
for (int i = topLocaleIndex + 1; i < inputLength; i++) {
|
||||
localeList[i] = (Locale) otherLocales.mList[i].clone();
|
||||
}
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < outputLength; i++) {
|
||||
sb.append(localeList[i].toLanguageTag());
|
||||
if (i < outputLength - 1) {
|
||||
sb.append(',');
|
||||
}
|
||||
}
|
||||
|
||||
mList = localeList;
|
||||
mStringRepresentation = sb.toString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LocaleList> CREATOR
|
||||
= new Parcelable.Creator<LocaleList>() {
|
||||
@Override
|
||||
@@ -196,7 +257,7 @@ public final class LocaleList implements Parcelable {
|
||||
} else {
|
||||
final String[] tags = list.split(",");
|
||||
final Locale[] localeArray = new Locale[tags.length];
|
||||
for (int i = 0; i < localeArray.length; ++i) {
|
||||
for (int i = 0; i < localeArray.length; i++) {
|
||||
localeArray[i] = Locale.forLanguageTag(tags[i]);
|
||||
}
|
||||
return new LocaleList(localeArray);
|
||||
@@ -227,6 +288,7 @@ public final class LocaleList implements Parcelable {
|
||||
return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
|
||||
}
|
||||
|
||||
@IntRange(from=0, to=1)
|
||||
private static int matchScore(Locale supported, Locale desired) {
|
||||
if (supported.equals(desired)) {
|
||||
return 1; // return early so we don't do unnecessary computation
|
||||
@@ -330,18 +392,79 @@ public final class LocaleList implements Parcelable {
|
||||
private final static Object sLock = new Object();
|
||||
|
||||
@GuardedBy("sLock")
|
||||
private static LocaleList sDefaultLocaleList;
|
||||
private static LocaleList sLastExplicitlySetLocaleList = null;
|
||||
@GuardedBy("sLock")
|
||||
private static LocaleList sDefaultLocaleList = null;
|
||||
@GuardedBy("sLock")
|
||||
private static Locale sLastDefaultLocale = null;
|
||||
|
||||
// TODO: fix this to return the default system locale list once we have that
|
||||
/**
|
||||
* The result is guaranteed to include the default Locale returned by Locale.getDefault(), but
|
||||
* not necessarily at the top of the list. The default locale not being at the top of the list
|
||||
* is an indication that the system has set the default locale to one of the user's other
|
||||
* preferred locales, having concluded that the primary preference is not supported but a
|
||||
* secondary preference is.
|
||||
*
|
||||
* Note that the default LocaleList would change if Locale.setDefault() is called. This method
|
||||
* takes that into account by always checking the output of Locale.getDefault() and adjusting
|
||||
* the default LocaleList if needed.
|
||||
*/
|
||||
@NonNull @Size(min=1)
|
||||
public static LocaleList getDefault() {
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
final Locale defaultLocale = Locale.getDefault();
|
||||
synchronized (sLock) {
|
||||
if (sDefaultLocaleList == null || sDefaultLocaleList.size() != 1
|
||||
|| !defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
|
||||
sDefaultLocaleList = new LocaleList(defaultLocale);
|
||||
if (!defaultLocale.equals(sLastDefaultLocale)) {
|
||||
sLastDefaultLocale = defaultLocale;
|
||||
// It's either the first time someone has asked for the default locale list, or
|
||||
// someone has called Locale.setDefault() since we last set or adjusted the default
|
||||
// locale list. So let's adjust the locale list.
|
||||
if (sDefaultLocaleList != null
|
||||
&& defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
|
||||
// The default Locale has changed, but it happens to be the first locale in the
|
||||
// default locale list, so we don't need to construct a new locale list.
|
||||
return sDefaultLocaleList;
|
||||
}
|
||||
sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList);
|
||||
}
|
||||
// sDefaultLocaleList can't be null, since it can't be set to null by
|
||||
// LocaleList.setDefault(), and if getDefault() is called before a call to
|
||||
// setDefault(), sLastDefaultLocale would be null and the check above would set
|
||||
// sDefaultLocaleList.
|
||||
return sDefaultLocaleList;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Also sets the default locale by calling Locale.setDefault() with the first locale in the
|
||||
* list.
|
||||
*
|
||||
* @throws NullPointerException if the input is <code>null</code>.
|
||||
* @throws IllegalArgumentException if the input is empty.
|
||||
*/
|
||||
public static void setDefault(@NonNull @Size(min=1) LocaleList locales) {
|
||||
setDefault(locales, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This may be used directly by system processes to set the default locale list for apps. For
|
||||
* such uses, the default locale list would always come from the user preferences, but the
|
||||
* default locale may have been chosen to be a locale other than the first locale in the locale
|
||||
* list (based on the locales the app supports).
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public static void setDefault(@NonNull @Size(min=1) LocaleList locales, int localeIndex) {
|
||||
if (locales == null) {
|
||||
throw new NullPointerException("locales is null");
|
||||
}
|
||||
if (locales.isEmpty()) {
|
||||
throw new IllegalArgumentException("locales is empty");
|
||||
}
|
||||
synchronized (sLock) {
|
||||
sLastDefaultLocale = locales.get(localeIndex);
|
||||
Locale.setDefault(sLastDefaultLocale);
|
||||
sLastExplicitlySetLocaleList = locales;
|
||||
sDefaultLocaleList = locales;
|
||||
}
|
||||
return sDefaultLocaleList;
|
||||
}
|
||||
}
|
||||
|
||||
84
core/tests/coretests/src/android/util/LocaleListTest.java
Normal file
84
core/tests/coretests/src/android/util/LocaleListTest.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.LocaleList;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class LocaleListTest extends TestCase {
|
||||
@SmallTest
|
||||
public void testConstructor() throws Exception {
|
||||
LocaleList ll;
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), null);
|
||||
assertEquals("fr", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.getEmptyLocaleList());
|
||||
assertEquals("fr", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr"));
|
||||
assertEquals("fr", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de"));
|
||||
assertEquals("fr,de", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,ja"));
|
||||
assertEquals("fr,de,ja", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr,ja"));
|
||||
assertEquals("fr,de,ja", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("de,fr"));
|
||||
assertEquals("fr,de", ll.toLanguageTags());
|
||||
|
||||
ll = new LocaleList(Locale.forLanguageTag("fr"), LocaleList.forLanguageTags("fr,de"));
|
||||
assertEquals("fr,de", ll.toLanguageTags());
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testConstructor_nullThrows() throws Exception {
|
||||
try {
|
||||
final LocaleList ll = new LocaleList(null, LocaleList.getEmptyLocaleList());
|
||||
fail("Constructing with locale and locale list should throw with a null locale.");
|
||||
} catch (Throwable e) {
|
||||
assertEquals(NullPointerException.class, e.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testGetDefault_localeSetDefaultCalledButNoChangeNecessary() throws Exception {
|
||||
final Locale originalLocale = Locale.getDefault();
|
||||
final LocaleList originalLocaleList = LocaleList.getDefault();
|
||||
final int originalLocaleIndex = originalLocaleList.indexOf(originalLocale);
|
||||
|
||||
// This simulates a situation potentially set by the system processes
|
||||
LocaleList.setDefault(LocaleList.forLanguageTags("ae,en,ja"), 1 /* en */);
|
||||
|
||||
// check our assumptions about input
|
||||
assertEquals("en", Locale.getDefault().toLanguageTag());
|
||||
final LocaleList firstResult = LocaleList.getDefault();
|
||||
assertEquals("ae,en,ja", LocaleList.getDefault().toLanguageTags());
|
||||
|
||||
Locale.setDefault(Locale.forLanguageTag("ae"));
|
||||
assertSame(firstResult, LocaleList.getDefault());
|
||||
|
||||
// restore the original values
|
||||
LocaleList.setDefault(originalLocaleList, originalLocaleIndex);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user