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
diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java
index 58b8eb0..bb2f948 100644
--- a/src/com/android/settings/InstrumentedFragment.java
+++ b/src/com/android/settings/InstrumentedFragment.java
@@ -37,6 +37,7 @@
     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 950b79c..0000000
--- 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 dab2cb7..88cfc74 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -80,6 +80,7 @@
 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 @@
             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 aa2d68a..9022538 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.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.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 @@
 
         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 @@
         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<LocalePicker.LocaleInfo> 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 @@
         @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 @@
 
             // 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 0000000..b1f68b9
--- /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<LocaleDragAndDropAdapter.CustomViewHolder> {
+
+    private final Context mContext;
+    private final List<LocaleStore.LocaleInfo> 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<LocaleStore.LocaleInfo> 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 0000000..8b1fa3c
--- /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 0000000..24e5e53
--- /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<LocaleStore.LocaleInfo> 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<LocaleStore.LocaleInfo> getUserLocaleList(Context context) {
+        List<LocaleStore.LocaleInfo> 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 0000000..b9c2b96
--- /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);
+    }
+}