Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2010 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 
     17 package com.android.internal.app;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.app.ActivityManager;
     22 import android.app.IActivityManager;
     23 import android.app.ListFragment;
     24 import android.app.backup.BackupManager;
     25 import android.content.Context;
     26 import android.content.res.Configuration;
     27 import android.content.res.Resources;
     28 import android.os.Bundle;
     29 import android.os.LocaleList;
     30 import android.os.RemoteException;
     31 import android.provider.Settings;
     32 import android.util.Log;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.widget.ArrayAdapter;
     37 import android.widget.ListView;
     38 import android.widget.TextView;
     39 
     40 import java.text.Collator;
     41 import java.util.Collections;
     42 import java.util.List;
     43 import java.util.Locale;
     44 import java.util.ArrayList;
     45 
     46 public class LocalePicker extends ListFragment {
     47     private static final String TAG = "LocalePicker";
     48     private static final boolean DEBUG = false;
     49     private static final String[] pseudoLocales = { "en-XA", "ar-XB" };
     50 
     51     public static interface LocaleSelectionListener {
     52         // You can add any argument if you really need it...
     53         public void onLocaleSelected(Locale locale);
     54     }
     55 
     56     LocaleSelectionListener mListener;  // default to null
     57 
     58     public static class LocaleInfo implements Comparable<LocaleInfo> {
     59         static final Collator sCollator = Collator.getInstance();
     60 
     61         String label;
     62         final Locale locale;
     63 
     64         public LocaleInfo(String label, Locale locale) {
     65             this.label = label;
     66             this.locale = locale;
     67         }
     68 
     69         public String getLabel() {
     70             return label;
     71         }
     72 
     73         public Locale getLocale() {
     74             return locale;
     75         }
     76 
     77         @Override
     78         public String toString() {
     79             return this.label;
     80         }
     81 
     82         @Override
     83         public int compareTo(LocaleInfo another) {
     84             return sCollator.compare(this.label, another.label);
     85         }
     86     }
     87 
     88     public static String[] getSystemAssetLocales() {
     89         return Resources.getSystem().getAssets().getLocales();
     90     }
     91 
     92     public static String[] getSupportedLocales(Context context) {
     93         return context.getResources().getStringArray(R.array.supported_locales);
     94     }
     95 
     96     public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
     97         final Resources resources = context.getResources();
     98 
     99         final String[] locales = getSystemAssetLocales();
    100         List<String> localeList = new ArrayList<String>(locales.length);
    101         Collections.addAll(localeList, locales);
    102 
    103         Collections.sort(localeList);
    104         final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
    105         final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
    106 
    107         final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
    108         for (String locale : localeList) {
    109             final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
    110             if (l == null || "und".equals(l.getLanguage())
    111                     || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
    112                 continue;
    113             }
    114             // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
    115             if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) {
    116                 continue;
    117             }
    118 
    119             if (localeInfos.isEmpty()) {
    120                 if (DEBUG) {
    121                     Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
    122                 }
    123                 localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
    124             } else {
    125                 // check previous entry:
    126                 //  same lang and a country -> upgrade to full name and
    127                 //    insert ours with full name
    128                 //  diff lang -> insert ours with lang-only name
    129                 final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
    130                 if (previous.locale.getLanguage().equals(l.getLanguage()) &&
    131                         !previous.locale.getLanguage().equals("zz")) {
    132                     if (DEBUG) {
    133                         Log.v(TAG, "backing up and fixing " + previous.label + " to " +
    134                                 getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
    135                     }
    136                     previous.label = toTitleCase(getDisplayName(
    137                             previous.locale, specialLocaleCodes, specialLocaleNames));
    138                     if (DEBUG) {
    139                         Log.v(TAG, "  and adding "+ toTitleCase(
    140                                 getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
    141                     }
    142                     localeInfos.add(new LocaleInfo(toTitleCase(
    143                             getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
    144                 } else {
    145                     String displayName = toTitleCase(l.getDisplayLanguage(l));
    146                     if (DEBUG) {
    147                         Log.v(TAG, "adding "+displayName);
    148                     }
    149                     localeInfos.add(new LocaleInfo(displayName, l));
    150                 }
    151             }
    152         }
    153 
    154         Collections.sort(localeInfos);
    155         return localeInfos;
    156     }
    157 
    158     /**
    159      * Constructs an Adapter object containing Locale information. Content is sorted by
    160      * {@link LocaleInfo#label}.
    161      */
    162     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
    163         return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
    164     }
    165 
    166     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
    167             final int layoutId, final int fieldId) {
    168         boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
    169                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
    170         final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
    171 
    172         final LayoutInflater inflater =
    173                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    174         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
    175             @Override
    176             public View getView(int position, View convertView, ViewGroup parent) {
    177                 View view;
    178                 TextView text;
    179                 if (convertView == null) {
    180                     view = inflater.inflate(layoutId, parent, false);
    181                     text = (TextView) view.findViewById(fieldId);
    182                     view.setTag(text);
    183                 } else {
    184                     view = convertView;
    185                     text = (TextView) view.getTag();
    186                 }
    187                 LocaleInfo item = getItem(position);
    188                 text.setText(item.toString());
    189                 text.setTextLocale(item.getLocale());
    190 
    191                 return view;
    192             }
    193         };
    194     }
    195 
    196     private static String toTitleCase(String s) {
    197         if (s.length() == 0) {
    198             return s;
    199         }
    200 
    201         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    202     }
    203 
    204     private static String getDisplayName(
    205             Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
    206         String code = l.toString();
    207 
    208         for (int i = 0; i < specialLocaleCodes.length; i++) {
    209             if (specialLocaleCodes[i].equals(code)) {
    210                 return specialLocaleNames[i];
    211             }
    212         }
    213 
    214         return l.getDisplayName(l);
    215     }
    216 
    217     @Override
    218     public void onActivityCreated(final Bundle savedInstanceState) {
    219         super.onActivityCreated(savedInstanceState);
    220         final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
    221         setListAdapter(adapter);
    222     }
    223 
    224     public void setLocaleSelectionListener(LocaleSelectionListener listener) {
    225         mListener = listener;
    226     }
    227 
    228     @Override
    229     public void onResume() {
    230         super.onResume();
    231         getListView().requestFocus();
    232     }
    233 
    234     /**
    235      * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale.
    236      *
    237      * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for
    238      * a moment and some callers won't want it.
    239      */
    240     @Override
    241     public void onListItemClick(ListView l, View v, int position, long id) {
    242         if (mListener != null) {
    243             final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
    244             mListener.onLocaleSelected(locale);
    245         }
    246     }
    247 
    248     /**
    249      * Requests the system to update the system locale. Note that the system looks halted
    250      * for a while during the Locale migration, so the caller need to take care of it.
    251      *
    252      * @see #updateLocales(LocaleList)
    253      */
    254     public static void updateLocale(Locale locale) {
    255         updateLocales(new LocaleList(locale));
    256     }
    257 
    258     /**
    259      * Requests the system to update the list of system locales.
    260      * Note that the system looks halted for a while during the Locale migration,
    261      * so the caller need to take care of it.
    262      */
    263     public static void updateLocales(LocaleList locales) {
    264         try {
    265             final IActivityManager am = ActivityManager.getService();
    266             final Configuration config = am.getConfiguration();
    267 
    268             config.setLocales(locales);
    269             config.userSetLocale = true;
    270 
    271             am.updatePersistentConfiguration(config);
    272             // Trigger the dirty bit for the Settings Provider.
    273             BackupManager.dataChanged("com.android.providers.settings");
    274         } catch (RemoteException e) {
    275             // Intentionally left blank
    276         }
    277     }
    278 
    279     /**
    280      * Get the locale list.
    281      *
    282      * @return The locale list.
    283      */
    284     public static LocaleList getLocales() {
    285         try {
    286             return ActivityManager.getService()
    287                     .getConfiguration().getLocales();
    288         } catch (RemoteException e) {
    289             // If something went wrong
    290             return LocaleList.getDefault();
    291         }
    292     }
    293 }
    294