1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.providers.contacts;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.icu.util.ULocale;
22import android.os.LocaleList;
23import android.text.TextUtils;
24
25import com.google.common.annotations.VisibleForTesting;
26import java.util.Locale;
27import java.util.Objects;
28
29public class LocaleSet {
30    private static final String SCRIPT_SIMPLIFIED_CHINESE = "Hans";
31    private static final String SCRIPT_TRADITIONAL_CHINESE = "Hant";
32
33    private final Locale mDefaultLocaleOverrideForTest;
34    private final LocaleList mLocaleList;
35
36    private LocaleSet(LocaleList localeList, Locale defaultLocaleOverrideForTest) {
37        mLocaleList = localeList;
38        mDefaultLocaleOverrideForTest = defaultLocaleOverrideForTest;
39    }
40
41    public static LocaleSet newDefault() {
42        return new LocaleSet(LocaleList.getDefault(),
43                /*defaultLocaleOverrideForTest= */ null);
44    }
45
46    @VisibleForTesting
47    public static LocaleSet newForTest(Locale... locales) {
48        return new LocaleSet(new LocaleList(locales), locales[0]);
49    }
50
51    @VisibleForTesting
52    static boolean isLanguageChinese(@Nullable Locale locale) {
53        return locale != null && "zh".equals(locale.getLanguage());
54    }
55
56    @VisibleForTesting
57    static boolean isLanguageJapanese(@Nullable Locale locale) {
58        return locale != null && "ja".equals(locale.getLanguage());
59    }
60
61    @VisibleForTesting
62    static boolean isLanguageKorean(@Nullable Locale locale) {
63        return locale != null && "ko".equals(locale.getLanguage());
64    }
65
66    @VisibleForTesting
67    static boolean isLocaleCJK(@Nullable Locale locale) {
68        return isLanguageChinese(locale) ||
69                isLanguageJapanese(locale) ||
70                isLanguageKorean(locale);
71    }
72
73    private static String getLikelyScript(Locale locale) {
74        final String script = locale.getScript();
75        if (!script.isEmpty()) {
76            return script;
77        } else {
78            return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript();
79        }
80    }
81
82    /**
83     * @return the script if the language is Chinese, and otherwise null.
84     */
85    @VisibleForTesting
86    static String getScriptIfChinese(@Nullable Locale locale) {
87        return isLanguageChinese(locale) ? getLikelyScript(locale) : null;
88    }
89
90    static boolean isLocaleSimplifiedChinese(@Nullable Locale locale) {
91        return SCRIPT_SIMPLIFIED_CHINESE.equals(getScriptIfChinese(locale));
92    }
93
94    @VisibleForTesting
95    static boolean isLocaleTraditionalChinese(@Nullable Locale locale) {
96        return SCRIPT_TRADITIONAL_CHINESE.equals(getScriptIfChinese(locale));
97    }
98
99    /**
100     * Returns the primary locale, which may not be the first item of {@link #getAllLocales}.
101     * (See {@link LocaleList})
102     */
103    public @NonNull Locale getPrimaryLocale() {
104        if (mDefaultLocaleOverrideForTest != null) {
105            return mDefaultLocaleOverrideForTest;
106        }
107        return Locale.getDefault();
108    }
109
110    public @NonNull LocaleList getAllLocales() {
111        return mLocaleList;
112    }
113
114    public boolean isPrimaryLocaleCJK() {
115        return isLocaleCJK(getPrimaryLocale());
116    }
117
118    /**
119     * @return true if Japanese is found in the list before simplified Chinese.
120     */
121    public boolean shouldPreferJapanese() {
122        if (isLanguageJapanese(getPrimaryLocale())) {
123            return true;
124        }
125        for (int i = 0; i < mLocaleList.size(); i++) {
126            final Locale l = mLocaleList.get(i);
127            if (isLanguageJapanese(l)) {
128                return true;
129            }
130            if (isLanguageChinese(l)) {
131                return false;
132            }
133        }
134        return false;
135    }
136
137    /**
138     * @return true if simplified Chinese is found before Japanese or traditional Chinese.
139     */
140    public boolean shouldPreferSimplifiedChinese() {
141        if (isLocaleSimplifiedChinese(getPrimaryLocale())) {
142            return true;
143        }
144        for (int i = 0; i < mLocaleList.size(); i++) {
145            final Locale l = mLocaleList.get(i);
146            if (isLocaleSimplifiedChinese(l)) {
147                return true;
148            }
149            if (isLanguageJapanese(l)) {
150                return false;
151            }
152            if (isLocaleTraditionalChinese(l)) { // Traditional chinese wins here.
153                return false;
154            }
155        }
156        return false;
157    }
158
159    /**
160     * @return true if the instance contains the current system locales.
161     */
162    public boolean isCurrent() {
163        return Objects.equals(mLocaleList, LocaleList.getDefault());
164    }
165
166    @Override
167    public boolean equals(Object object) {
168        if (object == this) {
169            return true;
170        }
171        if (object instanceof LocaleSet) {
172            final LocaleSet other = (LocaleSet) object;
173            return mLocaleList.equals(other.mLocaleList);
174        }
175        return false;
176    }
177
178    @Override
179    public final String toString() {
180        return mLocaleList.toString();
181    }
182}
183