Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(87)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/physicalweb/PwsClientImpl.java

Issue 2406203002: Use BCP47 compliant format for locale representation (Closed)
Patch Set: rebase master, unit tests added for LocaleUtils Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.physicalweb; 5 package org.chromium.chrome.browser.physicalweb;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.graphics.Bitmap; 8 import android.graphics.Bitmap;
9 import android.os.AsyncTask; 9 import android.os.AsyncTask;
10 import android.os.Build; 10 import android.os.Build;
11 import android.text.TextUtils; 11 import android.text.TextUtils;
12 12
13 import org.json.JSONArray; 13 import org.json.JSONArray;
14 import org.json.JSONException; 14 import org.json.JSONException;
15 import org.json.JSONObject; 15 import org.json.JSONObject;
16 16
17 import org.chromium.base.LocaleUtils;
17 import org.chromium.base.Log; 18 import org.chromium.base.Log;
18 import org.chromium.base.ThreadUtils; 19 import org.chromium.base.ThreadUtils;
19 import org.chromium.base.VisibleForTesting; 20 import org.chromium.base.VisibleForTesting;
20 import org.chromium.chrome.GoogleAPIKeys; 21 import org.chromium.chrome.GoogleAPIKeys;
21 import org.chromium.chrome.R; 22 import org.chromium.chrome.R;
22 import org.chromium.chrome.browser.ChromeVersionInfo; 23 import org.chromium.chrome.browser.ChromeVersionInfo;
23 import org.chromium.chrome.browser.physicalweb.PwsClient.FetchIconCallback; 24 import org.chromium.chrome.browser.physicalweb.PwsClient.FetchIconCallback;
24 import org.chromium.chrome.browser.physicalweb.PwsClient.ResolveScanCallback; 25 import org.chromium.chrome.browser.physicalweb.PwsClient.ResolveScanCallback;
25 26
26 import java.net.MalformedURLException; 27 import java.net.MalformedURLException;
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 Log.e(TAG, "Error making request to PWS%s", httpErr); 137 Log.e(TAG, "Error making request to PWS%s", httpErr);
137 resolveScanCallback.onPwsResults(new ArrayList<PwsResult>()); 138 resolveScanCallback.onPwsResults(new ArrayList<PwsResult>());
138 } 139 }
139 }; 140 };
140 141
141 // Create the request. 142 // Create the request.
142 HttpRequest request = null; 143 HttpRequest request = null;
143 try { 144 try {
144 JSONObject payload = createResolveScanPayload(broadcastUrls); 145 JSONObject payload = createResolveScanPayload(broadcastUrls);
145 String url = ENDPOINT_URL + "?key=" + getApiKey(); 146 String url = ENDPOINT_URL + "?key=" + getApiKey();
146 request = new JsonObjectHttpRequest(url, getUserAgent(), getAcceptLa nguage(), payload, 147 request = new JsonObjectHttpRequest(
147 requestCallback); 148 url, getUserAgent(), updateAcceptLanguage(), payload, reques tCallback);
148 } catch (MalformedURLException e) { 149 } catch (MalformedURLException e) {
149 Log.e(TAG, "Error creating PWS HTTP request", e); 150 Log.e(TAG, "Error creating PWS HTTP request", e);
150 return; 151 return;
151 } catch (JSONException e) { 152 } catch (JSONException e) {
152 Log.e(TAG, "Error creating PWS JSON payload", e); 153 Log.e(TAG, "Error creating PWS JSON payload", e);
153 return; 154 return;
154 } 155 }
155 // The callback will be called on the main thread. 156 // The callback will be called on the main thread.
156 AsyncTask.THREAD_POOL_EXECUTOR.execute(request); 157 AsyncTask.THREAD_POOL_EXECUTOR.execute(request);
157 } 158 }
(...skipping 21 matching lines...) Expand all
179 if (responseCode > 0) { 180 if (responseCode > 0) {
180 httpErr = ", HTTP " + responseCode; 181 httpErr = ", HTTP " + responseCode;
181 } 182 }
182 Log.e(TAG, "Error requesting icon%s", httpErr); 183 Log.e(TAG, "Error requesting icon%s", httpErr);
183 } 184 }
184 }; 185 };
185 186
186 // Create the request. 187 // Create the request.
187 BitmapHttpRequest request = null; 188 BitmapHttpRequest request = null;
188 try { 189 try {
189 request = new BitmapHttpRequest(iconUrl, getUserAgent(), getAcceptLa nguage(), 190 request = new BitmapHttpRequest(
190 requestCallback); 191 iconUrl, getUserAgent(), updateAcceptLanguage(), requestCall back);
191 } catch (MalformedURLException e) { 192 } catch (MalformedURLException e) {
192 Log.e(TAG, "Error creating icon request", e); 193 Log.e(TAG, "Error creating icon request", e);
193 return; 194 return;
194 } 195 }
195 // The callback will be called on the main thread. 196 // The callback will be called on the main thread.
196 AsyncTask.THREAD_POOL_EXECUTOR.execute(request); 197 AsyncTask.THREAD_POOL_EXECUTOR.execute(request);
197 } 198 }
198 199
199 /** 200 /**
200 * Recreate the Chrome for Android User-Agent string as closely as possible without calling any 201 * Recreate the Chrome for Android User-Agent string as closely as possible without calling any
(...skipping 14 matching lines...) Expand all
215 216
216 // Build the User-Agent string. 217 // Build the User-Agent string.
217 // eg: Mozilla/5.0 (Linux; Android 5.1.1; Nexus 4 Build/LMY48T) Appl eWebKit/0.0 (KHTML, 218 // eg: Mozilla/5.0 (Linux; Android 5.1.1; Nexus 4 Build/LMY48T) Appl eWebKit/0.0 (KHTML,
218 // like Gecko) Chrome/50.0.2661.89 Mobile Safari/0.0 219 // like Gecko) Chrome/50.0.2661.89 Mobile Safari/0.0
219 sUserAgent = String.format(USER_AGENT_FORMAT, osInfo, product); 220 sUserAgent = String.format(USER_AGENT_FORMAT, osInfo, product);
220 } 221 }
221 return sUserAgent; 222 return sUserAgent;
222 } 223 }
223 224
224 /** 225 /**
225 * Construct the Accept-Language string based on the current locale. 226 * Update an Accept-Language string based on the current default locales and make a string of
226 * @return An Accept-Language string. 227 * an Accept-Language header with q-values.
228 * @return An Accept-Language string made of an Accept-Language header with q-values.
227 */ 229 */
228 @VisibleForTesting 230 @VisibleForTesting
229 String getAcceptLanguage() { 231 String updateAcceptLanguage() {
230 String defaultLocale = Locale.getDefault().toString(); 232 String defaultLocale = LocaleUtils.getDefaultLocale();
231 if (sDefaultLocale == null || !sDefaultLocale.equals(defaultLocale)) { 233 if (sDefaultLocale == null || !sDefaultLocale.equals(defaultLocale)) {
232 String acceptLanguages = mContext.getResources().getString(R.string. accept_languages); 234 String acceptLanguages = mContext.getResources().getString(R.string. accept_languages);
233 acceptLanguages = prependToAcceptLanguagesIfNecessary(defaultLocale, acceptLanguages); 235 acceptLanguages = prependToAcceptLanguagesIfNecessary(defaultLocale, acceptLanguages);
234 sAcceptLanguage = generateAcceptLanguageHeader(acceptLanguages); 236 sAcceptLanguage = generateAcceptLanguageHeader(acceptLanguages);
235 sDefaultLocale = defaultLocale; 237 sDefaultLocale = defaultLocale;
236 } 238 }
237 return sAcceptLanguage; 239 return sAcceptLanguage;
238 } 240 }
239 241
240 /** 242 /**
241 * Handle the special cases in converting a language code/region code pair i nto an ISO-639-1
242 * language tag.
243 * @param language The 2-character language code
244 * @param region The 2-character country code
245 * @return A language tag.
246 */
247 @VisibleForTesting
248 static String makeLanguageTag(String language, String region) {
249 // Java mostly follows ISO-639-1 and ICU, except for the following three .
250 // See documentation on java.util.Locale constructor for more.
251 String isoLanguage;
252 if ("iw".equals(language)) {
253 isoLanguage = "he";
254 } else if ("ji".equals(language)) {
255 isoLanguage = "yi";
256 } else if ("in".equals(language)) {
257 isoLanguage = "id";
258 } else {
259 isoLanguage = language;
260 }
261
262 return isoLanguage + "-" + region;
263 }
264
265 /**
266 * Get the language code for the default locale and prepend it to the Accept -Language string if 243 * Get the language code for the default locale and prepend it to the Accept -Language string if
267 * it isn't already present. The logic should match PrependToAcceptLanguages IfNecessary in 244 * it isn't already present. The logic should match PrependToAcceptLanguages IfNecessary in
268 * chrome/browser/android/preferences/pref_service_bridge.cc 245 * chrome/browser/android/preferences/pref_service_bridge.cc
269 * @param locales A string representing a default locale or a list of defaul t locales. 246 * @param locales A string representing a default language tag or a list of default language
247 * tags.
270 * @param acceptLanguages The default language list for the language of the user's locale. 248 * @param acceptLanguages The default language list for the language of the user's locale.
271 * @return An updated language list. 249 * @return An updated language list.
272 */ 250 */
273 @VisibleForTesting 251 @VisibleForTesting
274 static String prependToAcceptLanguagesIfNecessary(String locales, String acc eptLanguages) { 252 static String prependToAcceptLanguagesIfNecessary(String locales, String acc eptLanguages) {
275 String localeString = locales + "," + acceptLanguages; 253 String localeStrings = locales + "," + acceptLanguages;
276 String[] localeList = localeString.split(","); 254 String[] localeList = localeStrings.split(",");
277 255
278 HashSet<String> seenLocales = new HashSet<>(); 256 HashSet<String> seenLanguageTags = new HashSet<>();
279 ArrayList<String> uniqueList = new ArrayList<>(); 257 ArrayList<String> uniqueList = new ArrayList<>();
280 for (String locale : localeList) { 258 for (String localeString : localeList) {
281 // TODO(yirui): Support BCP47 compliant format including 3-letter co untry code, 259 Locale locale = LocaleUtils.forLanguageTag(localeString);
282 // '-' separator and missing country case. 260 String language = locale.getLanguage();
283 if (locale.length() != 5 || (locale.charAt(2) != '_' && locale.charA t(2) != '-')) { 261 String country = locale.getCountry();
284 // Skip checking not well formed locales. 262 String languageTag = LocaleUtils.toLanguageTag(locale);
263
264 if (seenLanguageTags.contains(languageTag)) {
285 continue; 265 continue;
286 } 266 }
287 267 if (!country.isEmpty()) {
288 String language = locale.substring(0, 2); 268 seenLanguageTags.add(languageTag);
289 String country = locale.substring(3);
290 String languageTag = makeLanguageTag(language, country);
291
292 if (seenLocales.contains(languageTag)) {
293 continue;
294 } 269 }
295 uniqueList.add(languageTag); 270 uniqueList.add(languageTag);
Seigo Nonaka 2016/10/17 04:12:15 You are creating same locale object twice by calli
Yirui Huang 2016/10/17 10:40:52 Done.
296 seenLocales.add(languageTag);
297 } 271 }
298 272
299 // If language is not in the accept languages list, also add language co de. 273 // If language is not in the accept languages list, also add language co de.
300 // A language code should only be inserted after the last languageTag th at 274 // A language code should only be inserted after the last languageTag th at
301 // contains that language. 275 // contains that language.
302 // This will work with the IDS_ACCEPT_LANGUAGE localized strings bundled 276 // This will work with the IDS_ACCEPT_LANGUAGE localized strings bundled
303 // with Chrome but may fail on arbitrary lists of language tags due to 277 // with Chrome but may fail on arbitrary lists of language tags due to
304 // differences in case and whitespace. 278 // differences in case and whitespace.
305 HashSet<String> seenLanguages = new HashSet<>(); 279 HashSet<String> seenLanguages = new HashSet<>();
306 ArrayList<String> outputList = new ArrayList<>(); 280 ArrayList<String> outputList = new ArrayList<>();
307 for (int i = uniqueList.size() - 1; i >= 0; i--) { 281 for (int i = uniqueList.size() - 1; i >= 0; i--) {
308 String localeAdd = uniqueList.get(i); 282 String localeStr = uniqueList.get(i);
309 String languageAdd = localeAdd.substring(0, 2); 283 Locale localeAdd = LocaleUtils.forLanguageTag(localeStr);
284 String languageAdd = localeAdd.getLanguage();
285 String countryAdd = localeAdd.getCountry();
286
310 if (!seenLanguages.contains(languageAdd)) { 287 if (!seenLanguages.contains(languageAdd)) {
311 seenLanguages.add(languageAdd); 288 seenLanguages.add(languageAdd);
312 outputList.add(languageAdd); 289 outputList.add(languageAdd);
313 } 290 }
314 outputList.add(localeAdd); 291 if (!countryAdd.isEmpty()) {
292 outputList.add(localeStr);
293 }
315 } 294 }
316 Collections.reverse(outputList); 295 Collections.reverse(outputList);
317 return TextUtils.join(",", outputList); 296 return TextUtils.join(",", outputList);
318 } 297 }
319 298
320 /** 299 /**
321 * Given a list of comma-delimited language codes in decreasing order of pre ference, insert 300 * Given a list of comma-delimited language codes in decreasing order of pre ference, insert
322 * q-values to represent the relative quality/precedence of each language. T he logic should 301 * q-values to represent the relative quality/precedence of each language. T he logic should
323 * match GenerateAcceptLanguageHeader in net/http/http_util.cc. 302 * match GenerateAcceptLanguageHeader in net/http/http_util.cc.
324 * @param languageList A comma-delimited list of language codes containing n o whitespace. 303 * @param languageList A comma-delimited list of language codes containing n o whitespace.
(...skipping 15 matching lines...) Expand all
340 langListWithQ.format(",%s;q=0.%d", language, qvalue10); 319 langListWithQ.format(",%s;q=0.%d", language, qvalue10);
341 } 320 }
342 // It does not make sense to have 'q=0'. 321 // It does not make sense to have 'q=0'.
343 if (qvalue10 > kQvalueDecrement10) { 322 if (qvalue10 > kQvalueDecrement10) {
344 qvalue10 -= kQvalueDecrement10; 323 qvalue10 -= kQvalueDecrement10;
345 } 324 }
346 } 325 }
347 return langListWithQ.toString(); 326 return langListWithQ.toString();
348 } 327 }
349 } 328 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698