LocaleList.java revision 2b5ab1829476d839c24b06efaa92a6460bef3286
10ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader/* 20ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * Copyright (C) 2015 The Android Open Source Project 30ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * 40ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * Licensed under the Apache License, Version 2.0 (the "License"); 50ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * you may not use this file except in compliance with the License. 60ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * You may obtain a copy of the License at 70ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * 80ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * http://www.apache.org/licenses/LICENSE-2.0 90ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * 100ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * Unless required by applicable law or agreed to in writing, software 110ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * distributed under the License is distributed on an "AS IS" BASIS, 120ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * See the License for the specific language governing permissions and 140ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * limitations under the License. 150ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader */ 160ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 170ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournaderpackage android.util; 180ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 192b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournaderimport android.annotation.IntRange; 20a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournaderimport android.annotation.NonNull; 210ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournaderimport android.annotation.Nullable; 22a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournaderimport android.annotation.Size; 232591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournaderimport android.icu.util.ULocale; 24789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawaimport android.os.Parcel; 25789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawaimport android.os.Parcelable; 26a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader 27a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournaderimport com.android.internal.annotations.GuardedBy; 280ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 290ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournaderimport java.util.HashSet; 300ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournaderimport java.util.Locale; 310ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 320ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader/** 330ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * LocaleList is an immutable list of Locales, typically used to keep an 340ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * ordered user preferences for locales. 350ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader */ 36789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawapublic final class LocaleList implements Parcelable { 370ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader private final Locale[] mList; 38f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader // This is a comma-separated list of the locales in the LocaleList created at construction time, 39f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader // basically the result of running each locale's toLanguageTag() method and concatenating them 40f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader // with commas in between. 41789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @NonNull 42f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader private final String mStringRepresentation; 43f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader 440ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader private static final Locale[] sEmptyList = new Locale[0]; 45b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader private static final LocaleList sEmptyLocaleList = new LocaleList(); 460ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 470ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public Locale get(int location) { 480ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader return location < mList.length ? mList[location] : null; 490ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 500ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 518bca69858aefd2326b1bb7ead75796778ed54e93Roozbeh Pournader @Nullable 520ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public Locale getPrimary() { 530ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader return mList.length == 0 ? null : get(0); 540ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 550ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 560ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public boolean isEmpty() { 570ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader return mList.length == 0; 580ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 590ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 602b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader @IntRange(from=0) 610ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public int size() { 620ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader return mList.length; 630ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 640ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 652b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader @IntRange(from=-1) 662b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader public int indexOf(Locale locale) { 672b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < mList.length; i++) { 682b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (mList[i].equals(locale)) { 692b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader return i; 702b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 712b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 722b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader return -1; 732b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 742b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 75b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader @Override 76b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public boolean equals(Object other) { 77b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (other == this) 78b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return true; 79b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (!(other instanceof LocaleList)) 80b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return false; 81b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader final Locale[] otherList = ((LocaleList) other).mList; 82b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (mList.length != otherList.length) 83b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return false; 842b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < mList.length; i++) { 85b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (!mList[i].equals(otherList[i])) 86b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return false; 87b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 88b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return true; 89b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 90b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 91b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader @Override 92b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public int hashCode() { 93b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader int result = 1; 942b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < mList.length; i++) { 95b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader result = 31 * result + mList[i].hashCode(); 96b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 97b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return result; 98b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 99b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 100b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader @Override 101b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public String toString() { 102b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader StringBuilder sb = new StringBuilder(); 103b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader sb.append("["); 1042b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < mList.length; i++) { 105b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader sb.append(mList[i]); 106b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (i < mList.length - 1) { 107b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader sb.append(','); 108b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 109b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 110b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader sb.append("]"); 111b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return sb.toString(); 112b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 113b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 114789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @Override 115789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa public int describeContents() { 116789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa return 0; 117789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa } 118789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa 119789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @Override 120789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa public void writeToParcel(Parcel dest, int parcelableFlags) { 121789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa dest.writeString(mStringRepresentation); 122789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa } 123789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa 124f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader @NonNull 125b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public String toLanguageTags() { 126f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader return mStringRepresentation; 127b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 128b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 129b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader /** 130b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader * It is almost always better to call {@link #getEmptyLocaleList()} instead which returns 131b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader * a pre-constructed empty locale list. 132b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader */ 1330ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public LocaleList() { 1340ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader mList = sEmptyList; 135f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader mStringRepresentation = ""; 1360ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 1370ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader 1380ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader /** 1390ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * @throws NullPointerException if any of the input locales is <code>null</code>. 1400ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader * @throws IllegalArgumentException if any of the input locales repeat. 1410ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader */ 142b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public LocaleList(@Nullable Locale locale) { 143b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (locale == null) { 144b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader mList = sEmptyList; 145f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader mStringRepresentation = ""; 146b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } else { 147b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader mList = new Locale[1]; 148b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader mList[0] = (Locale) locale.clone(); 149f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader mStringRepresentation = locale.toLanguageTag(); 150b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 151b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 152b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 153b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader /** 154b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader * @throws NullPointerException if any of the input locales is <code>null</code>. 155b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader * @throws IllegalArgumentException if any of the input locales repeat. 156b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader */ 1570ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader public LocaleList(@Nullable Locale[] list) { 1580ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader if (list == null || list.length == 0) { 1590ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader mList = sEmptyList; 160f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader mStringRepresentation = ""; 1610ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } else { 1620ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader final Locale[] localeList = new Locale[list.length]; 1630ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader final HashSet<Locale> seenLocales = new HashSet<Locale>(); 164f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader final StringBuilder sb = new StringBuilder(); 1652b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < list.length; i++) { 1660ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader final Locale l = list[i]; 1670ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader if (l == null) { 1682b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader throw new NullPointerException("list[" + i + "] is null"); 1690ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } else if (seenLocales.contains(l)) { 1702b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader throw new IllegalArgumentException("list[" + i + "] is a repetition"); 1710ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } else { 172f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader final Locale localeClone = (Locale) l.clone(); 173f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader localeList[i] = localeClone; 174f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader sb.append(localeClone.toLanguageTag()); 175f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader if (i < list.length - 1) { 176f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader sb.append(','); 177f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader } 178f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader seenLocales.add(localeClone); 1790ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 1800ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 1810ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader mList = localeList; 182f036ead2a218ffa43697fcaa999b666a4c6d13cfRoozbeh Pournader mStringRepresentation = sb.toString(); 1830ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 1840ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader } 185b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 1862b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader /** 1872b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * Constructs a locale list, with the topLocale moved to the front if it already is 1882b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * in otherLocales, or added to the front if it isn't. 1892b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * 1902b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * {@hide} 1912b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader */ 1922b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader public LocaleList(@NonNull Locale topLocale, LocaleList otherLocales) { 1932b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (topLocale == null) { 1942b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader throw new NullPointerException("topLocale is null"); 1952b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 1962b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 1972b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader final int inputLength = (otherLocales == null) ? 0 : otherLocales.mList.length; 1982b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader int topLocaleIndex = -1; 1992b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < inputLength; i++) { 2002b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (topLocale.equals(otherLocales.mList[i])) { 2012b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader topLocaleIndex = i; 2022b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader break; 2032b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2042b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2052b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 2062b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader final int outputLength = inputLength + (topLocaleIndex == -1 ? 1 : 0); 2072b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader final Locale[] localeList = new Locale[outputLength]; 2082b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader localeList[0] = (Locale) topLocale.clone(); 2092b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (topLocaleIndex == -1) { 2102b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // topLocale was not in otherLocales 2112b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < inputLength; i++) { 2122b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader localeList[i + 1] = (Locale) otherLocales.mList[i].clone(); 2132b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2142b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } else { 2152b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < topLocaleIndex; i++) { 2162b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader localeList[i + 1] = (Locale) otherLocales.mList[i].clone(); 2172b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2182b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = topLocaleIndex + 1; i < inputLength; i++) { 2192b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader localeList[i] = (Locale) otherLocales.mList[i].clone(); 2202b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2212b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2222b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 2232b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader final StringBuilder sb = new StringBuilder(); 2242b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < outputLength; i++) { 2252b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sb.append(localeList[i].toLanguageTag()); 2262b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (i < outputLength - 1) { 2272b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sb.append(','); 2282b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2292b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2302b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 2312b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader mList = localeList; 2322b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader mStringRepresentation = sb.toString(); 2332b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 2342b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 235789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa public static final Parcelable.Creator<LocaleList> CREATOR 236789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa = new Parcelable.Creator<LocaleList>() { 237789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @Override 238789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa public LocaleList createFromParcel(Parcel source) { 239789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa return LocaleList.forLanguageTags(source.readString()); 240789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa } 241789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa 242789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @Override 243789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa public LocaleList[] newArray(int size) { 244789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa return new LocaleList[size]; 245789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa } 246789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa }; 247789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa 248789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @NonNull 249b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public static LocaleList getEmptyLocaleList() { 250b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return sEmptyLocaleList; 251b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 252b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader 253789d8fdbd9509e567cc3669c59e9e2dac2b57270Yohei Yukawa @NonNull 254b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader public static LocaleList forLanguageTags(@Nullable String list) { 255b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader if (list == null || list.equals("")) { 256b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return getEmptyLocaleList(); 257b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } else { 258b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader final String[] tags = list.split(","); 259b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader final Locale[] localeArray = new Locale[tags.length]; 2602b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader for (int i = 0; i < localeArray.length; i++) { 261b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader localeArray[i] = Locale.forLanguageTag(tags[i]); 262b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 263b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader return new LocaleList(localeArray); 264b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 265b46fdd427c5fc2ca69506cad0b35ea805477319dRoozbeh Pournader } 266a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader 2672591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader private static String getLikelyScript(Locale locale) { 2682591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader final String script = locale.getScript(); 2692591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (!script.isEmpty()) { 2702591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return script; 2712591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } else { 2722591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader // TODO: Cache the results if this proves to be too slow 2732591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript(); 2742591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 2752591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 2762591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader 2771c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static final String STRING_EN_XA = "en-XA"; 2781c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static final String STRING_AR_XB = "ar-XB"; 2791c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static final Locale LOCALE_EN_XA = new Locale("en", "XA"); 2801c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static final Locale LOCALE_AR_XB = new Locale("ar", "XB"); 2811c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static final int NUM_PSEUDO_LOCALES = 2; 2821c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader 2831c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static boolean isPseudoLocale(String locale) { 2841c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale); 2851c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 2861c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader 2871c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader private static boolean isPseudoLocale(Locale locale) { 2881c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale); 2891c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 2901c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader 2912b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader @IntRange(from=0, to=1) 2922591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader private static int matchScore(Locale supported, Locale desired) { 2932591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (supported.equals(desired)) { 2942591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return 1; // return early so we don't do unnecessary computation 2952591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 2962591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (!supported.getLanguage().equals(desired.getLanguage())) { 2972591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return 0; 2982591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 2991c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader if (isPseudoLocale(supported) || isPseudoLocale(desired)) { 3001c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // The locales are not the same, but the languages are the same, and one of the locales 3011c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // is a pseudo-locale. So this is not a match. 3021c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return 0; 3031c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 3042591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader // There is no match if the two locales use different scripts. This will most imporantly 3052591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader // take care of traditional vs simplified Chinese. 3062591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader final String supportedScr = getLikelyScript(supported); 3072591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader final String desiredScr = getLikelyScript(desired); 3082591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return supportedScr.equals(desiredScr) ? 1 : 0; 3092591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3102591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader 311fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader private static final Locale EN_LATN = Locale.forLanguageTag("en-Latn"); 312fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader 313fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader private Locale computeFirstMatch(String[] supportedLocales, boolean assumeEnglishIsSupported) { 3142591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (mList.length == 1) { // just one locale, perhaps the most common scenario 3152591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return mList[0]; 3162591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3172591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (mList.length == 0) { // empty locale list 3182591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return null; 3192591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3202591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader int bestIndex = Integer.MAX_VALUE; 321fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader final int numSupportedLocales = 322fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader supportedLocales.length + (assumeEnglishIsSupported ? 1 : 0); 323fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader for (int i = 0; i < numSupportedLocales; i++) { 324fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader final Locale supportedLocale; 325fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader if (assumeEnglishIsSupported) { 326fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader // Try English first, so we can return early if it's in the LocaleList 327fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader supportedLocale = (i == 0) ? EN_LATN : Locale.forLanguageTag(supportedLocales[i-1]); 328fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader } else { 329fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader supportedLocale = Locale.forLanguageTag(supportedLocales[i]); 330fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader } 3312591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader // We expect the average length of locale lists used for locale resolution to be 3322591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader // smaller than three, so it's OK to do this as an O(mn) algorithm. 3332591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader for (int idx = 0; idx < mList.length; idx++) { 3342591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader final int score = matchScore(supportedLocale, mList[idx]); 3352591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (score > 0) { 3362591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (idx == 0) { // We have a match on the first locale, which is good enough 3372591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return mList[0]; 3382591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } else if (idx < bestIndex) { 3392591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader bestIndex = idx; 3402591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3412591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3422591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3432591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3442591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader if (bestIndex == Integer.MAX_VALUE) { // no match was found 3452591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return mList[0]; 3462591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } else { 3472591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader return mList[bestIndex]; 3482591cc863018c3ca55f7725945a670f9f1c5e5fdRoozbeh Pournader } 3498bca69858aefd2326b1bb7ead75796778ed54e93Roozbeh Pournader } 3508bca69858aefd2326b1bb7ead75796778ed54e93Roozbeh Pournader 3511c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader /** 352fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * Returns the first match in the locale list given an unordered array of supported locales 353fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * in BCP47 format. 354fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * 355fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * If the locale list is empty, null would be returned. 356fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader */ 357fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader @Nullable 358fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader public Locale getFirstMatch(String[] supportedLocales) { 359fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader return computeFirstMatch(supportedLocales, false /* assume English is not supported */); 360fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader } 361fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader 362fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader /** 363fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * Same as getFirstMatch(), but with English assumed to be supported, even if it's not. 364fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader * {@hide} 365fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader */ 366fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader @Nullable 367fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader public Locale getFirstMatchWithEnglishSupported(String[] supportedLocales) { 368fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader return computeFirstMatch(supportedLocales, true /* assume English is supported */); 369fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader } 370fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader 371fb9236cb0c7bdad05ad01b722806edde7385b296Roozbeh Pournader /** 3721c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader * Returns true if the array of locale tags only contains empty locales and pseudolocales. 3731c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader * Assumes that there is no repetition in the input. 3741c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader * {@hide} 3751c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader */ 3761c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader public static boolean isPseudoLocalesOnly(String[] supportedLocales) { 3771c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) { 3781c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // This is for optimization. Since there's no repetition in the input, if we have more 3791c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // than the number of pseudo-locales plus one for the empty string, it's guaranteed 3801c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // that we have some meaninful locale in the list, so the list is not "practically 3811c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader // empty". 3821c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return false; 3831c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 3841c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader for (String locale : supportedLocales) { 3851c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader if (!locale.isEmpty() && !isPseudoLocale(locale)) { 3861c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return false; 3871c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 3881c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 3891c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader return true; 3901c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader } 3911c686f2ce6cbfa3fdb598f452aa31d38f3eb2320Roozbeh Pournader 392a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader private final static Object sLock = new Object(); 393a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader 394a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader @GuardedBy("sLock") 3952b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader private static LocaleList sLastExplicitlySetLocaleList = null; 3962b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader @GuardedBy("sLock") 3972b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader private static LocaleList sDefaultLocaleList = null; 3982b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader @GuardedBy("sLock") 3992b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader private static Locale sLastDefaultLocale = null; 400a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader 4012b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader /** 4022b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * The result is guaranteed to include the default Locale returned by Locale.getDefault(), but 4032b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * not necessarily at the top of the list. The default locale not being at the top of the list 4042b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * is an indication that the system has set the default locale to one of the user's other 4052b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * preferred locales, having concluded that the primary preference is not supported but a 4062b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * secondary preference is. 4072b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * 4082b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * Note that the default LocaleList would change if Locale.setDefault() is called. This method 4092b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * takes that into account by always checking the output of Locale.getDefault() and adjusting 4102b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * the default LocaleList if needed. 4112b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader */ 412a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader @NonNull @Size(min=1) 413a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader public static LocaleList getDefault() { 4142b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader final Locale defaultLocale = Locale.getDefault(); 415a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader synchronized (sLock) { 4162b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (!defaultLocale.equals(sLastDefaultLocale)) { 4172b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sLastDefaultLocale = defaultLocale; 4182b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // It's either the first time someone has asked for the default locale list, or 4192b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // someone has called Locale.setDefault() since we last set or adjusted the default 4202b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // locale list. So let's adjust the locale list. 4212b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (sDefaultLocaleList != null 4222b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader && defaultLocale.equals(sDefaultLocaleList.getPrimary())) { 4232b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // The default Locale has changed, but it happens to be the first locale in the 4242b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // default locale list, so we don't need to construct a new locale list. 4252b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader return sDefaultLocaleList; 4262b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4272b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sDefaultLocaleList = new LocaleList(defaultLocale, sLastExplicitlySetLocaleList); 428a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader } 4292b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // sDefaultLocaleList can't be null, since it can't be set to null by 4302b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // LocaleList.setDefault(), and if getDefault() is called before a call to 4312b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // setDefault(), sLastDefaultLocale would be null and the check above would set 4322b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader // sDefaultLocaleList. 4332b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader return sDefaultLocaleList; 4342b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4352b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4362b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 4372b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader /** 4382b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * Also sets the default locale by calling Locale.setDefault() with the first locale in the 4392b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * list. 4402b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * 4412b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * @throws NullPointerException if the input is <code>null</code>. 4422b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * @throws IllegalArgumentException if the input is empty. 4432b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader */ 4442b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader public static void setDefault(@NonNull @Size(min=1) LocaleList locales) { 4452b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader setDefault(locales, 0); 4462b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4472b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader 4482b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader /** 4492b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * This may be used directly by system processes to set the default locale list for apps. For 4502b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * such uses, the default locale list would always come from the user preferences, but the 4512b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * default locale may have been chosen to be a locale other than the first locale in the locale 4522b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * list (based on the locales the app supports). 4532b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * 4542b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader * {@hide} 4552b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader */ 4562b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader public static void setDefault(@NonNull @Size(min=1) LocaleList locales, int localeIndex) { 4572b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (locales == null) { 4582b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader throw new NullPointerException("locales is null"); 4592b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4602b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader if (locales.isEmpty()) { 4612b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader throw new IllegalArgumentException("locales is empty"); 4622b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader } 4632b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader synchronized (sLock) { 4642b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sLastDefaultLocale = locales.get(localeIndex); 4652b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader Locale.setDefault(sLastDefaultLocale); 4662b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sLastExplicitlySetLocaleList = locales; 4672b5ab1829476d839c24b06efaa92a6460bef3286Roozbeh Pournader sDefaultLocaleList = locales; 468a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader } 469a23748a9ff9ddc8b490fc31752afa9b955d5e156Roozbeh Pournader } 4700ba0d6bb188cf8cd279b0684b70c72323bf1fea2Roozbeh Pournader} 471