LocalePickerWithRegion.java revision df1ccbdc97a6540613c686298b82ea8385577c56
1/* 2 * Copyright (C) 2016 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 17package com.android.internal.app; 18 19import android.app.FragmentManager; 20import android.app.FragmentTransaction; 21import android.app.ListFragment; 22import android.content.Context; 23import android.os.Bundle; 24import android.util.LocaleList; 25import android.view.Menu; 26import android.view.MenuInflater; 27import android.view.MenuItem; 28import android.view.View; 29import android.widget.ListView; 30import android.widget.SearchView; 31 32import com.android.internal.R; 33 34import java.util.Collections; 35import java.util.HashSet; 36import java.util.Locale; 37import java.util.Set; 38 39/** 40 * A two-step locale picker. It shows a language, then a country. 41 * 42 * <p>It shows suggestions at the top, then the rest of the locales. 43 * Allows the user to search for locales using both their native name and their name in the 44 * default locale.</p> 45 */ 46public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener { 47 48 private SuggestedLocaleAdapter mAdapter; 49 private LocaleSelectedListener mListener; 50 private Set<LocaleStore.LocaleInfo> mLocaleList; 51 private LocaleStore.LocaleInfo mParentLocale; 52 private boolean mTranslatedOnly = false; 53 private boolean mCountryMode = false; 54 55 /** 56 * Other classes can register to be notified when a locale was selected. 57 * 58 * <p>This is the mechanism to "return" the result of the selection.</p> 59 */ 60 public interface LocaleSelectedListener { 61 /** 62 * The classes that want to retrieve the locale picked should implement this method. 63 * @param locale the locale picked. 64 */ 65 void onLocaleSelected(LocaleStore.LocaleInfo locale); 66 } 67 68 private static LocalePickerWithRegion createCountryPicker(Context context, 69 LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, 70 boolean translatedOnly) { 71 LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); 72 boolean shouldShowTheList = localePicker.setListener(context, listener, parent, 73 true /* country mode */, translatedOnly); 74 return shouldShowTheList ? localePicker : null; 75 } 76 77 public static LocalePickerWithRegion createLanguagePicker(Context context, 78 LocaleSelectedListener listener, boolean translatedOnly) { 79 LocalePickerWithRegion localePicker = new LocalePickerWithRegion(); 80 localePicker.setListener(context, listener, null, 81 false /* language mode */, translatedOnly); 82 return localePicker; 83 } 84 85 /** 86 * Sets the listener and initializes the locale list. 87 * 88 * <p>Returns true if we need to show the list, false if not.</p> 89 * 90 * <p>Can return false because of an error, trying to show a list of countries, 91 * but no parent locale was provided.</p> 92 * 93 * <p>It can also return false if the caller tries to show the list in country mode and 94 * there is only one country available (i.e. Japanese => Japan). 95 * In this case we don't even show the list, we call the listener with that locale, 96 * "pretending" it was selected, and return false.</p> 97 */ 98 private boolean setListener(Context context, LocaleSelectedListener listener, 99 LocaleStore.LocaleInfo parent, boolean countryMode, boolean translatedOnly) { 100 if (countryMode && (parent == null || parent.getLocale() == null)) { 101 // The list of countries is determined as all the countries where the parent language 102 // is used. 103 throw new IllegalArgumentException("The country selection list needs a parent."); 104 } 105 106 this.mCountryMode = countryMode; 107 this.mParentLocale = parent; 108 this.mListener = listener; 109 this.mTranslatedOnly = translatedOnly; 110 setRetainInstance(true); 111 112 final HashSet<String> langTagsToIgnore = new HashSet<>(); 113 if (!translatedOnly) { 114 final LocaleList userLocales = LocalePicker.getLocales(); 115 final String[] langTags = userLocales.toLanguageTags().split(","); 116 Collections.addAll(langTagsToIgnore, langTags); 117 } 118 119 if (countryMode) { 120 mLocaleList = LocaleStore.getLevelLocales(context, 121 langTagsToIgnore, parent, translatedOnly); 122 if (mLocaleList.size() <= 1) { 123 if (listener != null && (mLocaleList.size() == 1)) { 124 listener.onLocaleSelected(mLocaleList.iterator().next()); 125 } 126 return false; 127 } 128 } else { 129 mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, 130 null /* no parent */, translatedOnly); 131 } 132 133 return true; 134 } 135 136 @Override 137 public void onCreate(Bundle savedInstanceState) { 138 super.onCreate(savedInstanceState); 139 setHasOptionsMenu(true); 140 141 final Locale sortingLocale = (mCountryMode && mParentLocale != null) 142 ? mParentLocale.getLocale() 143 : Locale.getDefault(); 144 145 mAdapter = new SuggestedLocaleAdapter(mLocaleList, mCountryMode); 146 final LocaleHelper.LocaleInfoComparator comp = 147 new LocaleHelper.LocaleInfoComparator(sortingLocale); 148 mAdapter.sort(comp); 149 setListAdapter(mAdapter); 150 } 151 152 @Override 153 public boolean onOptionsItemSelected(MenuItem menuItem) { 154 int id = menuItem.getItemId(); 155 switch (id) { 156 case android.R.id.home: 157 getFragmentManager().popBackStack(); 158 return true; 159 } 160 return super.onOptionsItemSelected(menuItem); 161 } 162 163 @Override 164 public void onResume() { 165 super.onResume(); 166 167 if (mCountryMode) { 168 if (mParentLocale == null) { 169 this.getActivity().setTitle(R.string.country_selection_title); 170 } else { 171 this.getActivity().setTitle(mParentLocale.getFullNameNative()); 172 } 173 } else { 174 this.getActivity().setTitle(R.string.language_selection_title); 175 } 176 177 getListView().requestFocus(); 178 } 179 180 @Override 181 public void onListItemClick(ListView l, View v, int position, long id) { 182 final LocaleStore.LocaleInfo locale = 183 (LocaleStore.LocaleInfo) getListAdapter().getItem(position); 184 185 if (mCountryMode || locale.getParent() != null) { 186 if (mListener != null) { 187 mListener.onLocaleSelected(locale); 188 } 189 getFragmentManager().popBackStack("localeListEditor", 190 FragmentManager.POP_BACK_STACK_INCLUSIVE); 191 } else { 192 LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker( 193 getContext(), mListener, locale, mTranslatedOnly /* translate only */); 194 if (selector != null) { 195 getFragmentManager().beginTransaction() 196 .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) 197 .replace(getId(), selector).addToBackStack(null) 198 .commit(); 199 } else { 200 getFragmentManager().popBackStack("localeListEditor", 201 FragmentManager.POP_BACK_STACK_INCLUSIVE); 202 } 203 } 204 } 205 206 @Override 207 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 208 if (!mCountryMode) { 209 inflater.inflate(R.menu.language_selection_list, menu); 210 211 MenuItem mSearchMenuItem = menu.findItem(R.id.locale_search_menu); 212 SearchView mSearchView = (SearchView) mSearchMenuItem.getActionView(); 213 214 mSearchView.setQueryHint(getText(R.string.search_language_hint)); 215 mSearchView.setOnQueryTextListener(this); 216 mSearchView.setQuery("", false /* submit */); 217 } 218 } 219 220 @Override 221 public boolean onQueryTextSubmit(String query) { 222 return false; 223 } 224 225 @Override 226 public boolean onQueryTextChange(String newText) { 227 if (mAdapter != null) { 228 mAdapter.getFilter().filter(newText); 229 } 230 return false; 231 } 232} 233