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