From 930176b131194bc9e21b31d034f2d0dc2f2c80a8 Mon Sep 17 00:00:00 2001 From: Mihai Nita Date: Mon, 11 Apr 2016 08:00:28 -0700 Subject: [PATCH] Fix checkboxes and warning dialog lost when device rotates The frame is destroyed then the device rotates, so we need to save and restore the complete status (remove mode or not, list of checked locales, if the warning dialog it showing). Bug: 26720315 Bug: 26758726 Change-Id: Ie1340d3242cb61dc736a0b456224b8570826e937 --- .../LocaleDragAndDropAdapter.java | 43 ++++++++- .../localepicker/LocaleListEditor.java | 95 ++++++++++++++++--- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index a78c60dd099..2ce4e72f3d7 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -18,6 +18,7 @@ package com.android.settings.localepicker; 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.internal.app.LocaleStore; 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 @@ class LocaleDragAndDropAdapter extends RecyclerView.Adapter { private static final String TAG = "LocaleDragAndDropAdapter"; + private static final String CFGKEY_SELECTED_LOCALES = "selectedLocales"; private final Context mContext; private final List mFeedItemList; private final ItemTouchHelper mItemTouchHelper; @@ -105,6 +108,7 @@ class LocaleDragAndDropAdapter 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 @@ class LocaleDragAndDropAdapter 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 @@ class LocaleDragAndDropAdapter 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 @@ class LocaleDragAndDropAdapter 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 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 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 63649d75302..3287d393064 100644 --- a/src/com/android/settings/localepicker/LocaleListEditor.java +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -31,6 +31,7 @@ import android.view.MenuItem; 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 @@ import java.util.Locale; 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() { @@ -82,15 +86,45 @@ public class LocaleListEditor extends SettingsPreferenceFragment return result; } + @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 @@ public class LocaleListEditor extends SettingsPreferenceFragment 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 @@ public class LocaleListEditor extends SettingsPreferenceFragment // 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 class LocaleListEditor extends SettingsPreferenceFragment 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 @@ public class LocaleListEditor extends SettingsPreferenceFragment 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 @@ public class LocaleListEditor extends SettingsPreferenceFragment // 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); } }