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.ActivityManagerNative;
     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 String[] getPseudoLocales() {
     97         return pseudoLocales;
     98     }
     99 
    100     public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
    101         final Resources resources = context.getResources();
    102 
    103         final String[] locales = getSystemAssetLocales();
    104         List<String> localeList = new ArrayList<String>(locales.length);
    105         Collections.addAll(localeList, locales);
    106 
    107         // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
    108         if (!isInDeveloperMode) {
    109             for (String locale : pseudoLocales) {
    110                 localeList.remove(locale);
    111             }
    112         }
    113 
    114         Collections.sort(localeList);
    115         final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
    116         final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
    117 
    118         final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
    119         for (String locale : localeList) {
    120             final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
    121             if (l == null || "und".equals(l.getLanguage())
    122                     || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
    123                 continue;
    124             }
    125 
    126             if (localeInfos.isEmpty()) {
    127                 if (DEBUG) {
    128                     Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
    129                 }
    130                 localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
    131             } else {
    132                 // check previous entry:
    133                 //  same lang and a country -> upgrade to full name and
    134                 //    insert ours with full name
    135                 //  diff lang -> insert ours with lang-only name
    136                 final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
    137                 if (previous.locale.getLanguage().equals(l.getLanguage()) &&
    138                         !previous.locale.getLanguage().equals("zz")) {
    139                     if (DEBUG) {
    140                         Log.v(TAG, "backing up and fixing " + previous.label + " to " +
    141                                 getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
    142                     }
    143                     previous.label = toTitleCase(getDisplayName(
    144                             previous.locale, specialLocaleCodes, specialLocaleNames));
    145                     if (DEBUG) {
    146                         Log.v(TAG, "  and adding "+ toTitleCase(
    147                                 getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
    148                     }
    149                     localeInfos.add(new LocaleInfo(toTitleCase(
    150                             getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
    151                 } else {
    152                     String displayName = toTitleCase(l.getDisplayLanguage(l));
    153                     if (DEBUG) {
    154                         Log.v(TAG, "adding "+displayName);
    155                     }
    156                     localeInfos.add(new LocaleInfo(displayName, l));
    157                 }
    158             }
    159         }
    160 
    161         Collections.sort(localeInfos);
    162         return localeInfos;
    163     }
    164 
    165     /**
    166      * Constructs an Adapter object containing Locale information. Content is sorted by
    167      * {@link LocaleInfo#label}.
    168      */
    169     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
    170         return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
    171     }
    172 
    173     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
    174             final int layoutId, final int fieldId) {
    175         boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
    176                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
    177         final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
    178 
    179         final LayoutInflater inflater =
    180                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    181         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
    182             @Override
    183             public View getView(int position, View convertView, ViewGroup parent) {
    184                 View view;
    185                 TextView text;
    186                 if (convertView == null) {
    187                     view = inflater.inflate(layoutId, parent, false);
    188                     text = (TextView) view.findViewById(fieldId);
    189                     view.setTag(text);
    190                 } else {
    191                     view = convertView;
    192                     text = (TextView) view.getTag();
    193                 }
    194                 LocaleInfo item = getItem(position);
    195                 text.setText(item.toString());
    196                 text.setTextLocale(item.getLocale());
    197 
    198                 return view;
    199             }
    200         };
    201     }
    202 
    203     private static String toTitleCase(String s) {
    204         if (s.length() == 0) {
    205             return s;
    206         }
    207 
    208         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    209     }
    210 
    211     private static String getDisplayName(
    212             Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
    213         String code = l.toString();
    214 
    215         for (int i = 0; i < specialLocaleCodes.length; i++) {
    216             if (specialLocaleCodes[i].equals(code)) {
    217                 return specialLocaleNames[i];
    218             }
    219         }
    220 
    221         return l.getDisplayName(l);
    222     }
    223 
    224     @Override
    225     public void onActivityCreated(final Bundle savedInstanceState) {
    226         super.onActivityCreated(savedInstanceState);
    227         final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
    228         setListAdapter(adapter);
    229     }
    230 
    231     public void setLocaleSelectionListener(LocaleSelectionListener listener) {
    232         mListener = listener;
    233     }
    234 
    235     @Override
    236     public void onResume() {
    237         super.onResume();
    238         getListView().requestFocus();
    239     }
    240 
    241     /**
    242      * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale.
    243      *
    244      * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for
    245      * a moment and some callers won't want it.
    246      */
    247     @Override
    248     public void onListItemClick(ListView l, View v, int position, long id) {
    249         if (mListener != null) {
    250             final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
    251             mListener.onLocaleSelected(locale);
    252         }
    253     }
    254 
    255     /**
    256      * Requests the system to update the system locale. Note that the system looks halted
    257      * for a while during the Locale migration, so the caller need to take care of it.
    258      *
    259      * @see #updateLocales(LocaleList)
    260      */
    261     public static void updateLocale(Locale locale) {
    262         updateLocales(new LocaleList(locale));
    263     }
    264 
    265     /**
    266      * Requests the system to update the list of system locales.
    267      * Note that the system looks halted for a while during the Locale migration,
    268      * so the caller need to take care of it.
    269      */
    270     public static void updateLocales(LocaleList locales) {
    271         try {
    272             final IActivityManager am = ActivityManagerNative.getDefault();
    273             final Configuration config = am.getConfiguration();
    274 
    275             config.setLocales(locales);
    276             config.userSetLocale = true;
    277 
    278             am.updatePersistentConfiguration(config);
    279             // Trigger the dirty bit for the Settings Provider.
    280             BackupManager.dataChanged("com.android.providers.settings");
    281         } catch (RemoteException e) {
    282             // Intentionally left blank
    283         }
    284     }
    285 
    286     /**
    287      * Get the locale list.
    288      *
    289      * @return The locale list.
    290      */
    291     public static LocaleList getLocales() {
    292         try {
    293             return ActivityManagerNative.getDefault()
    294                     .getConfiguration().getLocales();
    295         } catch (RemoteException e) {
    296             // If something went wrong
    297             return LocaleList.getDefault();
    298         }
    299     }
    300 }
    301