|  | /* | 
|  | * Copyright (C) 2010 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 com.android.internal.app; | 
|  |  | 
|  | import com.android.internal.R; | 
|  |  | 
|  | import android.app.ActivityManager; | 
|  | import android.app.IActivityManager; | 
|  | import android.app.ListFragment; | 
|  | import android.app.backup.BackupManager; | 
|  | import android.content.Context; | 
|  | import android.content.res.Configuration; | 
|  | import android.content.res.Resources; | 
|  | import android.os.Bundle; | 
|  | import android.os.LocaleList; | 
|  | import android.os.RemoteException; | 
|  | import android.provider.Settings; | 
|  | import android.util.Log; | 
|  | import android.view.LayoutInflater; | 
|  | import android.view.View; | 
|  | import android.view.ViewGroup; | 
|  | import android.widget.ArrayAdapter; | 
|  | import android.widget.ListView; | 
|  | import android.widget.TextView; | 
|  |  | 
|  | import java.text.Collator; | 
|  | import java.util.Collections; | 
|  | import java.util.List; | 
|  | import java.util.Locale; | 
|  | import java.util.ArrayList; | 
|  |  | 
|  | public class LocalePicker extends ListFragment { | 
|  | private static final String TAG = "LocalePicker"; | 
|  | private static final boolean DEBUG = false; | 
|  | private static final String[] pseudoLocales = { "en-XA", "ar-XB" }; | 
|  |  | 
|  | public static interface LocaleSelectionListener { | 
|  | // You can add any argument if you really need it... | 
|  | public void onLocaleSelected(Locale locale); | 
|  | } | 
|  |  | 
|  | LocaleSelectionListener mListener;  // default to null | 
|  |  | 
|  | public static class LocaleInfo implements Comparable<LocaleInfo> { | 
|  | static final Collator sCollator = Collator.getInstance(); | 
|  |  | 
|  | String label; | 
|  | final Locale locale; | 
|  |  | 
|  | public LocaleInfo(String label, Locale locale) { | 
|  | this.label = label; | 
|  | this.locale = locale; | 
|  | } | 
|  |  | 
|  | public String getLabel() { | 
|  | return label; | 
|  | } | 
|  |  | 
|  | public Locale getLocale() { | 
|  | return locale; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return this.label; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int compareTo(LocaleInfo another) { | 
|  | return sCollator.compare(this.label, another.label); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static String[] getSystemAssetLocales() { | 
|  | return Resources.getSystem().getAssets().getLocales(); | 
|  | } | 
|  |  | 
|  | public static String[] getSupportedLocales(Context context) { | 
|  | return context.getResources().getStringArray(R.array.supported_locales); | 
|  | } | 
|  |  | 
|  | public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) { | 
|  | final Resources resources = context.getResources(); | 
|  |  | 
|  | final String[] locales = getSystemAssetLocales(); | 
|  | List<String> localeList = new ArrayList<String>(locales.length); | 
|  | Collections.addAll(localeList, locales); | 
|  |  | 
|  | Collections.sort(localeList); | 
|  | final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes); | 
|  | final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names); | 
|  |  | 
|  | final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size()); | 
|  | for (String locale : localeList) { | 
|  | final Locale l = Locale.forLanguageTag(locale.replace('_', '-')); | 
|  | if (l == null || "und".equals(l.getLanguage()) | 
|  | || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) { | 
|  | continue; | 
|  | } | 
|  | // Don't show the pseudolocales unless we're in developer mode. http://b/17190407. | 
|  | if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (localeInfos.isEmpty()) { | 
|  | if (DEBUG) { | 
|  | Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l))); | 
|  | } | 
|  | localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l)); | 
|  | } else { | 
|  | // check previous entry: | 
|  | //  same lang and a country -> upgrade to full name and | 
|  | //    insert ours with full name | 
|  | //  diff lang -> insert ours with lang-only name | 
|  | final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1); | 
|  | if (previous.locale.getLanguage().equals(l.getLanguage()) && | 
|  | !previous.locale.getLanguage().equals("zz")) { | 
|  | if (DEBUG) { | 
|  | Log.v(TAG, "backing up and fixing " + previous.label + " to " + | 
|  | getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames)); | 
|  | } | 
|  | previous.label = toTitleCase(getDisplayName( | 
|  | previous.locale, specialLocaleCodes, specialLocaleNames)); | 
|  | if (DEBUG) { | 
|  | Log.v(TAG, "  and adding "+ toTitleCase( | 
|  | getDisplayName(l, specialLocaleCodes, specialLocaleNames))); | 
|  | } | 
|  | localeInfos.add(new LocaleInfo(toTitleCase( | 
|  | getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l)); | 
|  | } else { | 
|  | String displayName = toTitleCase(l.getDisplayLanguage(l)); | 
|  | if (DEBUG) { | 
|  | Log.v(TAG, "adding "+displayName); | 
|  | } | 
|  | localeInfos.add(new LocaleInfo(displayName, l)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | Collections.sort(localeInfos); | 
|  | return localeInfos; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Constructs an Adapter object containing Locale information. Content is sorted by | 
|  | * {@link LocaleInfo#label}. | 
|  | */ | 
|  | public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) { | 
|  | return constructAdapter(context, R.layout.locale_picker_item, R.id.locale); | 
|  | } | 
|  |  | 
|  | public static ArrayAdapter<LocaleInfo> constructAdapter(Context context, | 
|  | final int layoutId, final int fieldId) { | 
|  | boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(), | 
|  | Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; | 
|  | final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode); | 
|  |  | 
|  | final LayoutInflater inflater = | 
|  | (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | 
|  | return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) { | 
|  | @Override | 
|  | public View getView(int position, View convertView, ViewGroup parent) { | 
|  | View view; | 
|  | TextView text; | 
|  | if (convertView == null) { | 
|  | view = inflater.inflate(layoutId, parent, false); | 
|  | text = (TextView) view.findViewById(fieldId); | 
|  | view.setTag(text); | 
|  | } else { | 
|  | view = convertView; | 
|  | text = (TextView) view.getTag(); | 
|  | } | 
|  | LocaleInfo item = getItem(position); | 
|  | text.setText(item.toString()); | 
|  | text.setTextLocale(item.getLocale()); | 
|  |  | 
|  | return view; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | private static String toTitleCase(String s) { | 
|  | if (s.length() == 0) { | 
|  | return s; | 
|  | } | 
|  |  | 
|  | return Character.toUpperCase(s.charAt(0)) + s.substring(1); | 
|  | } | 
|  |  | 
|  | private static String getDisplayName( | 
|  | Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) { | 
|  | String code = l.toString(); | 
|  |  | 
|  | for (int i = 0; i < specialLocaleCodes.length; i++) { | 
|  | if (specialLocaleCodes[i].equals(code)) { | 
|  | return specialLocaleNames[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | return l.getDisplayName(l); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onActivityCreated(final Bundle savedInstanceState) { | 
|  | super.onActivityCreated(savedInstanceState); | 
|  | final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity()); | 
|  | setListAdapter(adapter); | 
|  | } | 
|  |  | 
|  | public void setLocaleSelectionListener(LocaleSelectionListener listener) { | 
|  | mListener = listener; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onResume() { | 
|  | super.onResume(); | 
|  | getListView().requestFocus(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale. | 
|  | * | 
|  | * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for | 
|  | * a moment and some callers won't want it. | 
|  | */ | 
|  | @Override | 
|  | public void onListItemClick(ListView l, View v, int position, long id) { | 
|  | if (mListener != null) { | 
|  | final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale; | 
|  | mListener.onLocaleSelected(locale); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Requests the system to update the system locale. Note that the system looks halted | 
|  | * for a while during the Locale migration, so the caller need to take care of it. | 
|  | * | 
|  | * @see #updateLocales(LocaleList) | 
|  | */ | 
|  | public static void updateLocale(Locale locale) { | 
|  | updateLocales(new LocaleList(locale)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Requests the system to update the list of system locales. | 
|  | * Note that the system looks halted for a while during the Locale migration, | 
|  | * so the caller need to take care of it. | 
|  | */ | 
|  | public static void updateLocales(LocaleList locales) { | 
|  | try { | 
|  | final IActivityManager am = ActivityManager.getService(); | 
|  | final Configuration config = am.getConfiguration(); | 
|  |  | 
|  | config.setLocales(locales); | 
|  | config.userSetLocale = true; | 
|  |  | 
|  | am.updatePersistentConfiguration(config); | 
|  | // Trigger the dirty bit for the Settings Provider. | 
|  | BackupManager.dataChanged("com.android.providers.settings"); | 
|  | } catch (RemoteException e) { | 
|  | // Intentionally left blank | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the locale list. | 
|  | * | 
|  | * @return The locale list. | 
|  | */ | 
|  | public static LocaleList getLocales() { | 
|  | try { | 
|  | return ActivityManager.getService() | 
|  | .getConfiguration().getLocales(); | 
|  | } catch (RemoteException e) { | 
|  | // If something went wrong | 
|  | return LocaleList.getDefault(); | 
|  | } | 
|  | } | 
|  | } |