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