Merge "Fix checkboxes and warning dialog lost when device rotates" into nyc-dev
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index a78c60d..2ce4e72 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
+import android.os.Bundle;
 import android.support.v4.view.MotionEventCompat;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
@@ -36,6 +37,7 @@
 import com.android.settings.R;
 
 import java.text.NumberFormat;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -45,6 +47,7 @@
         extends RecyclerView.Adapter<LocaleDragAndDropAdapter.CustomViewHolder> {
 
     private static final String TAG = "LocaleDragAndDropAdapter";
+    private static final String CFGKEY_SELECTED_LOCALES = "selectedLocales";
     private final Context mContext;
     private final List<LocaleStore.LocaleInfo> mFeedItemList;
     private final ItemTouchHelper mItemTouchHelper;
@@ -105,6 +108,7 @@
             private static final int SELECTION_LOST = 0;
             private static final int SELECTION_UNCHANGED = -1;
             private int mSelectionStatus = SELECTION_UNCHANGED;
+
             @Override
             public void onChildDraw(Canvas c, RecyclerView recyclerView,
                     RecyclerView.ViewHolder viewHolder, float dX, float dY,
@@ -148,7 +152,6 @@
     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());
@@ -156,7 +159,7 @@
         dragCell.setShowCheckbox(mRemoveMode);
         dragCell.setShowMiniLabel(!mRemoveMode);
         dragCell.setShowHandle(!mRemoveMode && mDragEnabled);
-        dragCell.setChecked(false);
+        dragCell.setChecked(mRemoveMode ? feedItem.getChecked() : false);
         dragCell.setTag(feedItem);
         dragCell.getCheckbox()
                 .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@@ -286,4 +289,40 @@
     private void setDragEnabled(boolean enabled) {
         mDragEnabled = enabled;
     }
+
+    /**
+     * Saves the list of checked locales to preserve status when the list is destroyed.
+     * (for instance when the device is rotated)
+     * @param outInstanceState Bundle in which to place the saved state
+     */
+    public void saveState(Bundle outInstanceState) {
+        if (outInstanceState != null) {
+            final ArrayList<String> selectedLocales = new ArrayList<>();
+            for (LocaleStore.LocaleInfo li : mFeedItemList) {
+                if (li.getChecked()) {
+                    selectedLocales.add(li.getId());
+                }
+            }
+            outInstanceState.putStringArrayList(CFGKEY_SELECTED_LOCALES, selectedLocales);
+        }
+    }
+
+    /**
+     * Restores the list of checked locales to preserve status when the list is recreated.
+     * (for instance when the device is rotated)
+     * @param savedInstanceState Bundle with the data saved by {@link #saveState(Bundle)}
+     */
+    public void restoreState(Bundle savedInstanceState) {
+        if (savedInstanceState != null && mRemoveMode) {
+            final ArrayList<String> selectedLocales =
+                    savedInstanceState.getStringArrayList(CFGKEY_SELECTED_LOCALES);
+            if (selectedLocales == null || selectedLocales.isEmpty()) {
+                return;
+            }
+            for (LocaleStore.LocaleInfo li : mFeedItemList) {
+                li.setChecked(selectedLocales.contains(li.getId()));
+            }
+            notifyItemRangeChanged(0, mFeedItemList.size());
+        }
+    }
 }
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 63649d7..3287d39 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -31,6 +31,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
+
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.LocalePickerWithRegion;
 import com.android.internal.app.LocaleStore;
@@ -48,12 +49,15 @@
 public class LocaleListEditor extends SettingsPreferenceFragment
         implements LocalePickerWithRegion.LocaleSelectedListener {
 
+    private static final String CFGKEY_REMOVE_MODE = "localeRemoveMode";
+    private static final String CFGKEY_REMOVE_DIALOG = "showingLocaleRemoveDialog";
     private static final int MENU_ID_REMOVE = Menu.FIRST + 1;
 
     private LocaleDragAndDropAdapter mAdapter;
     private Menu mMenu;
-    private boolean mRemoveMode;
     private View mAddLanguage;
+    private boolean mRemoveMode;
+    private boolean mShowingRemoveDialog;
 
     @Override
     protected int getMetricsCategory() {
@@ -83,14 +87,44 @@
     }
 
     @Override
+    public void onViewStateRestored(Bundle savedInstanceState) {
+        super.onViewStateRestored(savedInstanceState);
+        if (savedInstanceState != null) {
+            mRemoveMode = savedInstanceState.getBoolean(CFGKEY_REMOVE_MODE, false);
+            mShowingRemoveDialog = savedInstanceState.getBoolean(CFGKEY_REMOVE_DIALOG, false);
+        }
+        setRemoveMode(mRemoveMode);
+        mAdapter.restoreState(savedInstanceState);
+
+        if (mShowingRemoveDialog) {
+            showRemoveLocaleWarningDialog();
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(CFGKEY_REMOVE_MODE, mRemoveMode);
+        outState.putBoolean(CFGKEY_REMOVE_DIALOG, mShowingRemoveDialog);
+        mAdapter.saveState(outState);
+    }
+
+    @Override
     public boolean onOptionsItemSelected(MenuItem menuItem) {
-        if (menuItem.getItemId() == MENU_ID_REMOVE) {
-            if (mRemoveMode) {
-                removeLocaleWarningDialog();
-            } else {
-                setRemoveMode(true);
-            }
-            return true;
+        switch (menuItem.getItemId()) {
+            case MENU_ID_REMOVE:
+                if (mRemoveMode) {
+                    showRemoveLocaleWarningDialog();
+                } else {
+                    setRemoveMode(true);
+                }
+                return true;
+            case android.R.id.home:
+                if (mRemoveMode) {
+                    setRemoveMode(false);
+                    return true;
+                }
+                break;
         }
         return super.onOptionsItemSelected(menuItem);
     }
@@ -98,12 +132,15 @@
     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);
+        updateVisibilityOfRemoveMenu();
     }
 
-    private void removeLocaleWarningDialog() {
+    // Show the appropriate warning when the user tries to remove locales.
+    // Shows no warning if there is no locale checked, shows a warning
+    // about removing all the locales if all of them are checked, and
+    // a "regular" warning otherwise.
+    private void showRemoveLocaleWarningDialog() {
         int checkedCount = mAdapter.getCheckedCount();
 
         // Nothing checked, just exit remove mode without a warning dialog
@@ -114,6 +151,7 @@
 
         // All locales selected, warning dialog, can't remove them all
         if (checkedCount == mAdapter.getItemCount()) {
+            mShowingRemoveDialog = true;
             new AlertDialog.Builder(getActivity())
                     .setTitle(R.string.dlg_remove_locales_error_title)
                     .setMessage(R.string.dlg_remove_locales_error_message)
@@ -122,6 +160,12 @@
                         public void onClick(DialogInterface dialog, int which) {
                         }
                     })
+                    .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                        @Override
+                        public void onDismiss(DialogInterface dialog) {
+                            mShowingRemoveDialog = false;
+                        }
+                    })
                     .create()
                     .show();
             return;
@@ -129,21 +173,38 @@
 
         final String title = getResources().getQuantityString(R.plurals.dlg_remove_locales_title,
                 checkedCount);
+        mShowingRemoveDialog = true;
         new AlertDialog.Builder(getActivity())
                 .setTitle(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);
+                        setRemoveMode(false);
                     }
                 })
                 .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
+                        // This is a sensitive area to change.
+                        // removeChecked() triggers a system update and "kills" the frame.
+                        // This means that saveState + restoreState are called before
+                        // setRemoveMode is called.
+                        // So we want that mRemoveMode and dialog status have the right values
+                        // before that save.
+                        // We can't just call setRemoveMode(false) before calling removeCheched
+                        // because that unchecks all items and removeChecked would have nothing
+                        // to remove.
+                        mRemoveMode = false;
+                        mShowingRemoveDialog = false;
                         mAdapter.removeChecked();
-                        setRemoveMode(!mRemoveMode);
-                        updateVisibilityOfRemoveMenu();
+                        setRemoveMode(false);
+                    }
+                })
+                .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        mShowingRemoveDialog = false;
                     }
                 })
                 .create()
@@ -208,8 +269,14 @@
     // Hide the "Remove" menu if there is only one locale in the list, show it otherwise
     // This is called when the menu is first created, and then one add / remove locale
     private void updateVisibilityOfRemoveMenu() {
+        if (mMenu == null) {
+            return;
+        }
+
         final MenuItem menuItemRemove = mMenu.findItem(MENU_ID_REMOVE);
         if (menuItemRemove != null) {
+            menuItemRemove.setShowAsAction(
+                    mRemoveMode ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
             menuItemRemove.setVisible(mAdapter.getItemCount() > 1);
         }
     }