From d7d48fda53747a7a25e903d9ef6defa6ea590ebb Mon Sep 17 00:00:00 2001 From: Mihai Nita Date: Tue, 12 Jan 2016 08:55:05 -0800 Subject: [PATCH] Implement multiple locales and sublocales in Settings Does drag-and-drop reordering, supports adding / removing locales, suggestions, search, removes locales already selected in the user preferences. Bug: 25800339 Change-Id: Iffe7b9810c77ec93a84d848ab20b2ba405249676 --- AndroidManifest.xml | 2 +- res/drawable/drag_handle.xml | 29 +++ res/layout/locale_drag_cell.xml | 79 +++++++ res/layout/locale_order_list.xml | 44 ++++ res/values/strings.xml | 23 +- res/xml/language_settings.xml | 6 +- .../settings/InstrumentedFragment.java | 1 + src/com/android/settings/LocalePicker.java | 115 ---------- .../android/settings/SettingsActivity.java | 3 +- .../InputMethodAndLanguageSettings.java | 32 ++- .../LocaleDragAndDropAdapter.java | 203 +++++++++++++++++ .../settings/localepicker/LocaleDragCell.java | 101 +++++++++ .../localepicker/LocaleListEditor.java | 204 ++++++++++++++++++ .../localepicker/LocaleRecyclerView.java | 47 ++++ 14 files changed, 747 insertions(+), 142 deletions(-) create mode 100644 res/drawable/drag_handle.xml create mode 100644 res/layout/locale_drag_cell.xml create mode 100644 res/layout/locale_order_list.xml delete mode 100644 src/com/android/settings/LocalePicker.java create mode 100644 src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java create mode 100644 src/com/android/settings/localepicker/LocaleDragCell.java create mode 100644 src/com/android/settings/localepicker/LocaleListEditor.java create mode 100644 src/com/android/settings/localepicker/LocaleRecyclerView.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3281adedf3f..ad783d3a523 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -567,7 +567,7 @@ + android:value="com.android.settings.localepicker.LocaleListEditor" /> diff --git a/res/drawable/drag_handle.xml b/res/drawable/drag_handle.xml new file mode 100644 index 00000000000..c01ee1cf1ba --- /dev/null +++ b/res/drawable/drag_handle.xml @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/res/layout/locale_drag_cell.xml b/res/layout/locale_drag_cell.xml new file mode 100644 index 00000000000..978e0adf9cc --- /dev/null +++ b/res/layout/locale_drag_cell.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + diff --git a/res/layout/locale_order_list.xml b/res/layout/locale_order_list.xml new file mode 100644 index 00000000000..97ba9a3a864 --- /dev/null +++ b/res/layout/locale_order_list.xml @@ -0,0 +1,44 @@ + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index db68d71f224..7a5d768002d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -513,8 +513,27 @@ Next - - Language + + + Language preference + + + Remove + + + Add a language + + + Remove from your language preference? + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla egestas porta aliquet. Ut laoreet orci tellus, id cursus enim volutpat in. + + + Language removal error + + Can’t remove all the languages, you should leave at least one. + Choose activity diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml index fa96a56fbfa..83a0985bc90 100644 --- a/res/xml/language_settings.xml +++ b/res/xml/language_settings.xml @@ -4,9 +4,9 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,7 +22,7 @@ diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java index 58b8eb0b81d..bb2f948105f 100644 --- a/src/com/android/settings/InstrumentedFragment.java +++ b/src/com/android/settings/InstrumentedFragment.java @@ -37,6 +37,7 @@ public abstract class InstrumentedFragment extends PreferenceFragment { public static final int DATA_USAGE_LIST = UNDECLARED + 7; public static final int BILLING_CYCLE = UNDECLARED + 8; public static final int APP_DATA_USAGE = UNDECLARED + 9; + public static final int USER_LOCALE_LIST = UNDECLARED + 10; /** * Declare the view of this category. diff --git a/src/com/android/settings/LocalePicker.java b/src/com/android/settings/LocalePicker.java deleted file mode 100644 index 950b79c38a6..00000000000 --- a/src/com/android/settings/LocalePicker.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings; - -import android.app.Dialog; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ListView; - -import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; - -import java.util.Locale; - -public class LocalePicker extends com.android.internal.app.LocalePickerWithRegion - implements com.android.internal.app.LocalePickerWithRegion.LocaleSelectionListener, - DialogCreatable { - - private static final String TAG = "LocalePicker"; - - private SettingsDialogFragment mDialogFragment; - private static final int DLG_SHOW_GLOBAL_WARNING = 1; - private static final String SAVE_TARGET_LOCALE = "locale"; - - private Locale mTargetLocale; - - public LocalePicker() { - super(); - setLocaleSelectionListener(this); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_TARGET_LOCALE)) { - mTargetLocale = new Locale(savedInstanceState.getString(SAVE_TARGET_LOCALE)); - } - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = super.onCreateView(inflater, container, savedInstanceState); - final ListView list = (ListView) view.findViewById(android.R.id.list); - Utils.forcePrepareCustomPreferencesList(container, view, list, false); - return view; - } - - @Override - public void onLocaleSelected(final Locale locale) { - if (Utils.hasMultipleUsers(getActivity())) { - mTargetLocale = locale; - showDialog(DLG_SHOW_GLOBAL_WARNING); - } else { - getActivity().onBackPressed(); - com.android.internal.app.LocalePicker.updateLocale(locale); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - if (mTargetLocale != null) { - outState.putString(SAVE_TARGET_LOCALE, mTargetLocale.toString()); - } - } - - protected void showDialog(int dialogId) { - if (mDialogFragment != null) { - Log.e(TAG, "Old dialog fragment not null!"); - } - mDialogFragment = new SettingsDialogFragment(this, dialogId); - mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); - } - - public Dialog onCreateDialog(final int dialogId) { - return Utils.buildGlobalChangeWarningDialog(getActivity(), - R.string.global_locale_change_title, - new Runnable() { - public void run() { - removeDialog(dialogId); - getActivity().onBackPressed(); - com.android.internal.app.LocalePicker.updateLocale(mTargetLocale); - } - } - ); - } - - protected void removeDialog(int dialogId) { - // mDialogFragment may not be visible yet in parent fragment's onResume(). - // To be able to dismiss dialog at that time, don't check - // mDialogFragment.isVisible(). - if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) { - mDialogFragment.dismiss(); - } - mDialogFragment = null; - } -} diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index dab2cb7607c..88cfc748d73 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -80,6 +80,7 @@ import com.android.settings.inputmethod.InputMethodAndLanguageSettings; import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; import com.android.settings.inputmethod.SpellCheckersSettings; import com.android.settings.inputmethod.UserDictionaryList; +import com.android.settings.localepicker.LocaleListEditor; import com.android.settings.location.LocationSettings; import com.android.settings.nfc.AndroidBeam; import com.android.settings.nfc.PaymentSettings; @@ -246,7 +247,7 @@ public class SettingsActivity extends SettingsDrawerActivity WifiP2pSettings.class.getName(), VpnSettings.class.getName(), DateTimeSettings.class.getName(), - LocalePicker.class.getName(), + LocaleListEditor.class.getName(), InputMethodAndLanguageSettings.class.getName(), SpellCheckersSettings.class.getName(), UserDictionaryList.class.getName(), diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java index aa2d68a8cd7..9022538c094 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java +++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java @@ -43,6 +43,7 @@ import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; +import android.util.LocaleList; import android.view.InputDevice; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -50,6 +51,7 @@ import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.TextServicesManager; +import com.android.internal.app.LocaleHelper; import com.android.internal.app.LocalePicker; import com.android.internal.logging.MetricsLogger; import com.android.settings.R; @@ -266,8 +268,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment if (!mShowsOnlyFullImeAndKeyboardList) { if (mLanguagePref != null) { - String localeName = getLocaleName(getActivity()); - mLanguagePref.setSummary(localeName); + String localeNames = getLocaleNames(getActivity()); + mLanguagePref.setSummary(localeNames); } updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); @@ -340,19 +342,9 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment return super.onPreferenceTreeClick(preference); } - private static String getLocaleName(Context context) { - // We want to show the same string that the LocalePicker used. - // TODO: should this method be in LocalePicker instead? - Locale currentLocale = context.getResources().getConfiguration().locale; - List locales = LocalePicker.getAllAssetLocales(context, true); - for (LocalePicker.LocaleInfo locale : locales) { - if (locale.getLocale().equals(currentLocale)) { - return locale.getLabel(); - } - } - // This can't happen as long as the locale was one set by Settings. - // Fall back in case a developer is testing an unsupported locale. - return currentLocale.getDisplayName(currentLocale); + private static String getLocaleNames(Context context) { + final LocaleList locales = LocalePicker.getLocales(); + return LocaleHelper.getDisplayLocaleList(locales, Locale.getDefault()); } private void saveInputMethodSelectorVisibility(String value) { @@ -665,8 +657,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment @Override public void setListening(boolean listening) { if (listening) { - String localeName = getLocaleName(mContext); - mSummaryLoader.setSummary(this, localeName); + String localeNames = getLocaleNames(mContext); + mSummaryLoader.setSummary(this, localeNames); } } } @@ -690,12 +682,12 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment // Locale picker. if (context.getAssets().getLocales().length > 1) { - String localeName = getLocaleName(context); + String localeNames = getLocaleNames(context); SearchIndexableRaw indexable = new SearchIndexableRaw(context); indexable.key = KEY_PHONE_LANGUAGE; indexable.title = context.getString(R.string.phone_language); - indexable.summaryOn = localeName; - indexable.summaryOff = localeName; + indexable.summaryOn = localeNames; + indexable.summaryOff = localeNames; indexable.screenTitle = screenTitle; indexables.add(indexable); } diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java new file mode 100644 index 00000000000..b1f68b91e75 --- /dev/null +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.localepicker; + +import android.content.Context; +import android.support.v4.view.MotionEventCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.util.LocaleList; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; + +import com.android.settings.R; + +import com.android.internal.app.LocalePicker; +import com.android.internal.app.LocaleStore; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + + +class LocaleDragAndDropAdapter + extends RecyclerView.Adapter { + + private final Context mContext; + private final List mFeedItemList; + private final ItemTouchHelper mItemTouchHelper; + private boolean mRemoveMode = false; + private boolean mDragEnabled = true; + + class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener { + private final LocaleDragCell mLocaleDragCell; + + public CustomViewHolder(LocaleDragCell view) { + super(view); + mLocaleDragCell = view; + mLocaleDragCell.getDragHandle().setOnTouchListener(this); + mLocaleDragCell.getTextLabel().setOnTouchListener(this); + mLocaleDragCell.getTranslateableLabel().setOnTouchListener(this); + } + + public LocaleDragCell getLocaleDragCell() { + return mLocaleDragCell; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mDragEnabled) { + switch (MotionEventCompat.getActionMasked(event)) { + case MotionEvent.ACTION_DOWN: + mItemTouchHelper.startDrag(this); + } + } + return false; + } + } + + public LocaleDragAndDropAdapter(Context context, List feedItemList) { + this.mFeedItemList = feedItemList; + + this.mContext = context; + this.mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0 /* no swipe */) { + + @Override + public boolean onMove(RecyclerView view, RecyclerView.ViewHolder source, + RecyclerView.ViewHolder target) { + onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); + return true; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) { + // Swipe is disabled, this is intentionally empty. + } + }); + } + + public void setRecyclerView(RecyclerView rv) { + mItemTouchHelper.attachToRecyclerView(rv); + } + + @Override + public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + final LocaleDragCell item = (LocaleDragCell) LayoutInflater.from(mContext) + .inflate(R.layout.locale_drag_cell, viewGroup, false); + return new CustomViewHolder(item); + } + + @Override + public void onBindViewHolder(final CustomViewHolder holder, int i) { + final LocaleStore.LocaleInfo feedItem = mFeedItemList.get(i); + final LocaleDragCell dragCell = holder.getLocaleDragCell(); + + String label = feedItem.getFullNameNative(); + dragCell.setLabel(label); + dragCell.setLocalized(feedItem.isTranslated()); + dragCell.setMiniLabel(Integer.toString(i + 1)); + dragCell.setShowCheckbox(mRemoveMode); + dragCell.setShowMiniLabel(!mRemoveMode); + dragCell.setShowHandle(!mRemoveMode); + dragCell.setChecked(false); + dragCell.setTag(feedItem); + dragCell.getCheckbox() + .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + LocaleStore.LocaleInfo feedItem = + (LocaleStore.LocaleInfo) holder.getLocaleDragCell().getTag(); + feedItem.setChecked(isChecked); + } + }); + } + + @Override + public int getItemCount() { + int itemCount = (null != mFeedItemList ? mFeedItemList.size() : 0); + if (itemCount < 2 || mRemoveMode) { + setDragEnabled(false); + } else { + setDragEnabled(true); + } + return itemCount; + } + + private void onItemMove(int fromPosition, int toPosition) { + Collections.swap(mFeedItemList, fromPosition, toPosition); + notifyItemChanged(fromPosition); // to update the numbers + notifyItemChanged(toPosition); + notifyItemMoved(fromPosition, toPosition); + } + + void setRemoveMode(boolean removeMode) { + mRemoveMode = removeMode; + int itemCount = mFeedItemList.size(); + for (int i = 0; i < itemCount; i++) { + mFeedItemList.get(i).setChecked(false); + notifyItemChanged(i); + } + } + + void removeChecked() { + int itemCount = mFeedItemList.size(); + for (int i = itemCount - 1; i >= 0; i--) { + if (mFeedItemList.get(i).getChecked()) { + mFeedItemList.remove(i); + } + } + notifyDataSetChanged(); + doTheUpdate(); + } + + int getCheckedCount() { + int result = 0; + for (LocaleStore.LocaleInfo li : mFeedItemList) { + if (li.getChecked()) { + result++; + } + } + return result; + } + + void addLocale(LocaleStore.LocaleInfo li) { + mFeedItemList.add(li); + notifyItemInserted(mFeedItemList.size() - 1); + doTheUpdate(); + } + + public void doTheUpdate() { + int count = mFeedItemList.size(); + Locale[] newList = new Locale[count]; + + for (int i = 0; i < count; i++) { + LocaleStore.LocaleInfo li = mFeedItemList.get(i); + newList[i] = li.getLocale(); + } + + LocaleList ll = new LocaleList(newList); + LocalePicker.updateLocales(ll); + } + + private void setDragEnabled(boolean enabled) { + mDragEnabled = enabled; + } +} diff --git a/src/com/android/settings/localepicker/LocaleDragCell.java b/src/com/android/settings/localepicker/LocaleDragCell.java new file mode 100644 index 00000000000..8b1fa3c5f8f --- /dev/null +++ b/src/com/android/settings/localepicker/LocaleDragCell.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.localepicker; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.settings.R; + +class LocaleDragCell extends LinearLayout { + private TextView mLabel; + private TextView mMiniLabel; + private ImageView mLocalized; + private CheckBox mCheckbox; + private ImageView mDragHandle; + + public LocaleDragCell(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mLabel = (TextView) findViewById(R.id.label); + mLocalized = (ImageView) findViewById(R.id.l10nWarn); + mMiniLabel = (TextView) findViewById(R.id.miniLabel); + mCheckbox = (CheckBox) findViewById(R.id.checkbox); + mDragHandle = (ImageView) findViewById(R.id.dragHandle); + } + + public void setShowHandle(boolean showHandle) { + mDragHandle.setVisibility(showHandle ? VISIBLE : GONE); + invalidate(); + requestLayout(); + } + + public void setShowCheckbox(boolean showCheckbox) { + mCheckbox.setVisibility(showCheckbox ? VISIBLE : GONE); + invalidate(); + requestLayout(); + } + + public void setChecked(boolean checked) { + mCheckbox.setChecked(checked); + } + + public void setShowMiniLabel(boolean showMiniLabel) { + mMiniLabel.setVisibility(showMiniLabel ? VISIBLE : GONE); + invalidate(); + requestLayout(); + } + + public void setMiniLabel(String miniLabelText) { + mMiniLabel.setText(miniLabelText); + invalidate(); + } + + public void setLabel(String labelText) { + mLabel.setText(labelText); + invalidate(); + } + + public void setLocalized(boolean localized) { + mLocalized.setVisibility(localized ? INVISIBLE : VISIBLE); + invalidate(); + } + + public ImageView getDragHandle() { + return mDragHandle; + } + + public ImageView getTranslateableLabel() { + return mLocalized; + } + + public TextView getTextLabel() { + return mLabel; + } + + public CheckBox getCheckbox() { + return mCheckbox; + } +} diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java new file mode 100644 index 00000000000..24e5e53d24a --- /dev/null +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.localepicker; + +import android.app.AlertDialog; +import android.app.FragmentTransaction; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.preference.PreferenceFragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.LocaleList; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import com.android.settings.R; + +import com.android.internal.app.LocalePicker; +import com.android.internal.app.LocalePickerWithRegion; +import com.android.internal.app.LocaleStore; +import com.android.settings.InstrumentedFragment; +import com.android.settings.SettingsPreferenceFragment; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Drag-and-drop editor for the user-ordered locale lists. + */ +public class LocaleListEditor extends SettingsPreferenceFragment + implements LocalePickerWithRegion.LocaleSelectedListener { + + private static final int MENU_ID_REMOVE = Menu.FIRST + 1; + + private LocaleDragAndDropAdapter mAdapter; + private Menu mMenu; + private boolean mRemoveMode; + private View mAddLanguage; + + @Override + protected int getMetricsCategory() { + return InstrumentedFragment.USER_LOCALE_LIST; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + + LocaleStore.fillCache(this.getContext()); + List feedsList = getUserLocaleList(this.getContext()); + mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) { + View result = super.onCreateView(inflater, container, savedInstState); + LinearLayout ll = (LinearLayout) result; + View myLayout = inflater.inflate(R.layout.locale_order_list, ll); + + getActivity().setTitle(R.string.pref_title_lang_selection); + + configureDragAndDrop(myLayout); + return result; + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + if (menuItem.getItemId() == MENU_ID_REMOVE) { + if (mRemoveMode) { + removeLocaleWarningDialog(); + } else { + setRemoveMode(true); + } + return true; + } + return super.onOptionsItemSelected(menuItem); + } + + private void setRemoveMode(boolean mRemoveMode) { + this.mRemoveMode = mRemoveMode; + mAdapter.setRemoveMode(mRemoveMode); + mMenu.findItem(MENU_ID_REMOVE).setShowAsAction( + mRemoveMode ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER); + mAddLanguage.setVisibility(mRemoveMode ? View.INVISIBLE : View.VISIBLE); + } + + private void removeLocaleWarningDialog() { + int checked = mAdapter.getCheckedCount(); + + // Nothing checked, just exit remove mode without a warning dialog + if (checked == 0) { + setRemoveMode(!mRemoveMode); + return; + } + + // All locales selected, warning dialog, can't remove them all + if (checked == mAdapter.getItemCount()) { + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.dlg_remove_locales_error_title) + .setMessage(R.string.dlg_remove_locales_error_message) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .create() + .show(); + return; + } + + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.dlg_remove_locales_title) + .setMessage(R.string.dlg_remove_locales_message) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + setRemoveMode(!mRemoveMode); + } + }) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mAdapter.removeChecked(); + setRemoveMode(!mRemoveMode); + } + }) + .create() + .show(); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + final MenuItem menuItem = + menu.add(Menu.NONE, MENU_ID_REMOVE, 0, R.string.locale_remove_menu); + menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menuItem.setIcon(android.R.drawable.ic_menu_delete); + super.onCreateOptionsMenu(menu, inflater); + mMenu = menu; + } + + private static List getUserLocaleList(Context context) { + List result = new ArrayList<>(); + + LocaleList localeList = LocalePicker.getLocales(); + for (int i = 0; i < localeList.size(); i++) { + Locale locale = localeList.get(i); + result.add(LocaleStore.getLocaleInfo(locale)); + } + + return result; + } + + private void configureDragAndDrop(View view) { + RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList); + list.setLayoutManager(new LinearLayoutManager(this.getContext())); + + list.setHasFixedSize(true); + mAdapter.setRecyclerView(list); + list.setAdapter(mAdapter); + + mAddLanguage = view.findViewById(R.id.add_language); + mAddLanguage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker( + getContext(), LocaleListEditor.this, false /* translate only */); + getFragmentManager() + .beginTransaction() + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .replace(getId(), selector) + .addToBackStack("localeListEditor") + .commit(); + } + }); + } + + @Override + public void onLocaleSelected(LocaleStore.LocaleInfo locale) { + mAdapter.addLocale(locale); + } + +} diff --git a/src/com/android/settings/localepicker/LocaleRecyclerView.java b/src/com/android/settings/localepicker/LocaleRecyclerView.java new file mode 100644 index 00000000000..b9c2b96b159 --- /dev/null +++ b/src/com/android/settings/localepicker/LocaleRecyclerView.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.localepicker; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.MotionEvent; + +class LocaleRecyclerView extends RecyclerView { + public LocaleRecyclerView(Context context) { + super(context); + } + + public LocaleRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LocaleRecyclerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + if (e.getAction() == MotionEvent.ACTION_UP) { + LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter(); + if (adapter != null) { + adapter.doTheUpdate(); + } + } + return super.onTouchEvent(e); + } +}