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 17 package com.android.settings.localepicker; 18 19 import android.app.AlertDialog; 20 import android.app.FragmentTransaction; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.os.Bundle; 24 import android.os.LocaleList; 25 import android.support.v7.widget.RecyclerView; 26 import android.view.LayoutInflater; 27 import android.view.Menu; 28 import android.view.MenuInflater; 29 import android.view.MenuItem; 30 import android.view.View; 31 import android.view.ViewGroup; 32 33 import com.android.internal.app.LocalePicker; 34 import com.android.internal.app.LocalePickerWithRegion; 35 import com.android.internal.app.LocaleStore; 36 import com.android.internal.logging.MetricsProto.MetricsEvent; 37 import com.android.settings.R; 38 import com.android.settings.SettingsPreferenceFragment; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Locale; 43 44 /** 45 * Drag-and-drop editor for the user-ordered locale lists. 46 */ 47 public class LocaleListEditor extends SettingsPreferenceFragment 48 implements LocalePickerWithRegion.LocaleSelectedListener { 49 50 private static final String CFGKEY_REMOVE_MODE = "localeRemoveMode"; 51 private static final String CFGKEY_REMOVE_DIALOG = "showingLocaleRemoveDialog"; 52 private static final int MENU_ID_REMOVE = Menu.FIRST + 1; 53 54 private LocaleDragAndDropAdapter mAdapter; 55 private Menu mMenu; 56 private View mAddLanguage; 57 private boolean mRemoveMode; 58 private boolean mShowingRemoveDialog; 59 60 @Override getMetricsCategory()61 protected int getMetricsCategory() { 62 return MetricsEvent.USER_LOCALE_LIST; 63 } 64 65 @Override onCreate(Bundle savedInstanceState)66 public void onCreate(Bundle savedInstanceState) { 67 super.onCreate(savedInstanceState); 68 setHasOptionsMenu(true); 69 70 LocaleStore.fillCache(this.getContext()); 71 final List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList(this.getContext()); 72 mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList); 73 } 74 75 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState)76 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) { 77 final View result = super.onCreateView(inflater, container, savedInstState); 78 final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result); 79 80 getActivity().setTitle(R.string.pref_title_lang_selection); 81 82 configureDragAndDrop(myLayout); 83 return result; 84 } 85 86 @Override onViewStateRestored(Bundle savedInstanceState)87 public void onViewStateRestored(Bundle savedInstanceState) { 88 super.onViewStateRestored(savedInstanceState); 89 if (savedInstanceState != null) { 90 mRemoveMode = savedInstanceState.getBoolean(CFGKEY_REMOVE_MODE, false); 91 mShowingRemoveDialog = savedInstanceState.getBoolean(CFGKEY_REMOVE_DIALOG, false); 92 } 93 setRemoveMode(mRemoveMode); 94 mAdapter.restoreState(savedInstanceState); 95 96 if (mShowingRemoveDialog) { 97 showRemoveLocaleWarningDialog(); 98 } 99 } 100 101 @Override onSaveInstanceState(Bundle outState)102 public void onSaveInstanceState(Bundle outState) { 103 super.onSaveInstanceState(outState); 104 outState.putBoolean(CFGKEY_REMOVE_MODE, mRemoveMode); 105 outState.putBoolean(CFGKEY_REMOVE_DIALOG, mShowingRemoveDialog); 106 mAdapter.saveState(outState); 107 } 108 109 @Override onOptionsItemSelected(MenuItem menuItem)110 public boolean onOptionsItemSelected(MenuItem menuItem) { 111 switch (menuItem.getItemId()) { 112 case MENU_ID_REMOVE: 113 if (mRemoveMode) { 114 showRemoveLocaleWarningDialog(); 115 } else { 116 setRemoveMode(true); 117 } 118 return true; 119 case android.R.id.home: 120 if (mRemoveMode) { 121 setRemoveMode(false); 122 return true; 123 } 124 break; 125 } 126 return super.onOptionsItemSelected(menuItem); 127 } 128 setRemoveMode(boolean mRemoveMode)129 private void setRemoveMode(boolean mRemoveMode) { 130 this.mRemoveMode = mRemoveMode; 131 mAdapter.setRemoveMode(mRemoveMode); 132 mAddLanguage.setVisibility(mRemoveMode ? View.INVISIBLE : View.VISIBLE); 133 updateVisibilityOfRemoveMenu(); 134 } 135 136 // Show the appropriate warning when the user tries to remove locales. 137 // Shows no warning if there is no locale checked, shows a warning 138 // about removing all the locales if all of them are checked, and 139 // a "regular" warning otherwise. showRemoveLocaleWarningDialog()140 private void showRemoveLocaleWarningDialog() { 141 int checkedCount = mAdapter.getCheckedCount(); 142 143 // Nothing checked, just exit remove mode without a warning dialog 144 if (checkedCount == 0) { 145 setRemoveMode(!mRemoveMode); 146 return; 147 } 148 149 // All locales selected, warning dialog, can't remove them all 150 if (checkedCount == mAdapter.getItemCount()) { 151 mShowingRemoveDialog = true; 152 new AlertDialog.Builder(getActivity()) 153 .setTitle(R.string.dlg_remove_locales_error_title) 154 .setMessage(R.string.dlg_remove_locales_error_message) 155 .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 156 @Override 157 public void onClick(DialogInterface dialog, int which) { 158 } 159 }) 160 .setOnDismissListener(new DialogInterface.OnDismissListener() { 161 @Override 162 public void onDismiss(DialogInterface dialog) { 163 mShowingRemoveDialog = false; 164 } 165 }) 166 .create() 167 .show(); 168 return; 169 } 170 171 final String title = getResources().getQuantityString(R.plurals.dlg_remove_locales_title, 172 checkedCount); 173 mShowingRemoveDialog = true; 174 new AlertDialog.Builder(getActivity()) 175 .setTitle(title) 176 .setMessage(R.string.dlg_remove_locales_message) 177 .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { 178 @Override 179 public void onClick(DialogInterface dialog, int which) { 180 setRemoveMode(false); 181 } 182 }) 183 .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 184 @Override 185 public void onClick(DialogInterface dialog, int which) { 186 // This is a sensitive area to change. 187 // removeChecked() triggers a system update and "kills" the frame. 188 // This means that saveState + restoreState are called before 189 // setRemoveMode is called. 190 // So we want that mRemoveMode and dialog status have the right values 191 // before that save. 192 // We can't just call setRemoveMode(false) before calling removeCheched 193 // because that unchecks all items and removeChecked would have nothing 194 // to remove. 195 mRemoveMode = false; 196 mShowingRemoveDialog = false; 197 mAdapter.removeChecked(); 198 setRemoveMode(false); 199 } 200 }) 201 .setOnDismissListener(new DialogInterface.OnDismissListener() { 202 @Override 203 public void onDismiss(DialogInterface dialog) { 204 mShowingRemoveDialog = false; 205 } 206 }) 207 .create() 208 .show(); 209 } 210 211 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)212 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 213 final MenuItem menuItem = 214 menu.add(Menu.NONE, MENU_ID_REMOVE, 0, R.string.locale_remove_menu); 215 menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT); 216 menuItem.setIcon(R.drawable.ic_delete); 217 super.onCreateOptionsMenu(menu, inflater); 218 mMenu = menu; 219 updateVisibilityOfRemoveMenu(); 220 } 221 getUserLocaleList(Context context)222 private static List<LocaleStore.LocaleInfo> getUserLocaleList(Context context) { 223 final List<LocaleStore.LocaleInfo> result = new ArrayList<>(); 224 225 final LocaleList localeList = LocalePicker.getLocales(); 226 for (int i = 0; i < localeList.size(); i++) { 227 Locale locale = localeList.get(i); 228 result.add(LocaleStore.getLocaleInfo(locale)); 229 } 230 231 return result; 232 } 233 configureDragAndDrop(View view)234 private void configureDragAndDrop(View view) { 235 final RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList); 236 final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter); 237 llm.setAutoMeasureEnabled(true); 238 list.setLayoutManager(llm); 239 240 list.setHasFixedSize(true); 241 mAdapter.setRecyclerView(list); 242 list.setAdapter(mAdapter); 243 244 mAddLanguage = view.findViewById(R.id.add_language); 245 mAddLanguage.setOnClickListener(new View.OnClickListener() { 246 @Override 247 public void onClick(View v) { 248 final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker( 249 getContext(), LocaleListEditor.this, false /* translate only */); 250 getFragmentManager() 251 .beginTransaction() 252 .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) 253 .replace(getId(), selector) 254 .addToBackStack("localeListEditor") 255 .commit(); 256 } 257 }); 258 } 259 260 @Override onLocaleSelected(LocaleStore.LocaleInfo locale)261 public void onLocaleSelected(LocaleStore.LocaleInfo locale) { 262 mAdapter.addLocale(locale); 263 updateVisibilityOfRemoveMenu(); 264 } 265 266 // Hide the "Remove" menu if there is only one locale in the list, show it otherwise 267 // This is called when the menu is first created, and then one add / remove locale updateVisibilityOfRemoveMenu()268 private void updateVisibilityOfRemoveMenu() { 269 if (mMenu == null) { 270 return; 271 } 272 273 final MenuItem menuItemRemove = mMenu.findItem(MENU_ID_REMOVE); 274 if (menuItemRemove != null) { 275 menuItemRemove.setShowAsAction( 276 mRemoveMode ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER); 277 menuItemRemove.setVisible(mAdapter.getItemCount() > 1); 278 } 279 } 280 } 281