Bug 1220928 - Handle configuration changes. r=sebastian draft
authorChenxia Liu <liuche@mozilla.com>
Mon, 28 Mar 2016 17:02:16 -0700
changeset 346118 a1e2892f208ff846fb5417dba41207832ca09a09
parent 346117 02e8bd0c063bbc336a9734f030999a669529d085
child 346119 bc8f0afe56966d96154ec86d9e4cea3237dadd26
child 346136 ad6f036ee85185947e1293e166ad9831e06096f8
push id14243
push usercliu@mozilla.com
push dateWed, 30 Mar 2016 23:23:30 +0000
reviewerssebastian
bugs1220928
milestone48.0a1
Bug 1220928 - Handle configuration changes. r=sebastian MozReview-Commit-ID: E5MoWlcMCdj
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryAdapter.java
@@ -42,45 +42,45 @@ public class CombinedHistoryAdapter exte
 
         public static int itemTypeToViewType(ItemType itemType) {
             return itemType.ordinal();
         }
     }
 
     private List<RemoteClient> remoteClients = Collections.emptyList();
     private List<RemoteTab> clientChildren;
+    private int remoteClientIndexOfParent = -1;
     private Cursor historyCursor;
 
     // Maintain group collapsed and hidden state. Only accessed from the UI thread.
     protected static RemoteTabsExpandableListState sState;
 
     // List of hidden remote clients.
     // Only accessed from the UI thread.
     protected final List<RemoteClient> hiddenClients = new ArrayList<>();
 
     // We use a sparse array to store each section header's position in the panel [more cheaply than a HashMap].
     private final SparseArray<CombinedHistoryPanel.SectionHeader> sectionHeaders;
 
     private final Context context;
 
-    private boolean inChildView = false;
-
-    public CombinedHistoryAdapter(Context context) {
+    public CombinedHistoryAdapter(Context context, int savedParentIndex) {
         super();
         this.context = context;
         sectionHeaders = new SparseArray<>();
 
         // This races when multiple Fragments are created. That's okay: one
         // will win, and thereafter, all will be okay. If we create and then
         // drop an instance the shared SharedPreferences backing all the
         // instances will maintain the state for us. Since everything happens on
         // the UI thread, this doesn't even need to be volatile.
         if (sState == null) {
             sState = new RemoteTabsExpandableListState(GeckoSharedPrefs.forProfile(context));
         }
+        remoteClientIndexOfParent = savedParentIndex;
     }
 
     public void setClients(List<RemoteClient> clients) {
         hiddenClients.clear();
         remoteClients.clear();
 
         final Iterator<RemoteClient> it = clients.iterator();
         while (it.hasNext()) {
@@ -179,32 +179,40 @@ public class CombinedHistoryAdapter exte
             for (int i = 1; i < clientChildren.size(); i++) {
                 urls.put(clientChildren.get(i).url);
             }
             return urls;
         }
         return null;
     }
 
+    public int getParentIndex() {
+        return remoteClientIndexOfParent;
+    }
+
+    private boolean isInChildView() {
+        return remoteClientIndexOfParent != -1;
+    }
+
     public void showChildView(int parentPosition) {
         if (clientChildren == null) {
             clientChildren = new ArrayList<>();
         }
         // Handle "back" view.
         clientChildren.add(null);
-        clientChildren.addAll(remoteClients.get(transformAdapterPositionForDataStructure(ItemType.CLIENT, parentPosition)).tabs);
-        inChildView = true;
+        remoteClientIndexOfParent = transformAdapterPositionForDataStructure(ItemType.CLIENT, parentPosition);
+        clientChildren.addAll(remoteClients.get(remoteClientIndexOfParent).tabs);
         notifyDataSetChanged();
     }
 
     public boolean exitChildView() {
-        if (!inChildView) {
+        if (!isInChildView()) {
             return false;
         }
-        inChildView = false;
+        remoteClientIndexOfParent = -1;
         clientChildren.clear();
         notifyDataSetChanged();
         return true;
     }
 
     private ItemType getItemTypeForPosition(int position) {
         return ItemType.viewTypeToItemType(getItemViewType(position));
     }
@@ -280,17 +288,17 @@ public class CombinedHistoryAdapter exte
                 return new CombinedHistoryItem.HistoryItem(view);
             default:
                 throw new IllegalArgumentException("Unexpected Home Panel item type");
         }
     }
 
     @Override
     public int getItemViewType(int position) {
-        if (inChildView) {
+        if (isInChildView()) {
             if (position == 0) {
                 return ItemType.itemTypeToViewType(ItemType.NAVIGATION_BACK);
             }
             return ItemType.itemTypeToViewType(ItemType.CHILD);
         } else {
             final int numClients = remoteClients.size();
             if (position < numClients) {
                 if (!hiddenClients.isEmpty() && position == numClients - 1) {
@@ -305,18 +313,23 @@ public class CombinedHistoryAdapter exte
             }
 
             return ItemType.itemTypeToViewType(ItemType.HISTORY);
         }
     }
 
     @Override
     public int getItemCount() {
-        if (inChildView) {
-            return (clientChildren == null) ? 0 : clientChildren.size();
+        if (isInChildView()) {
+            if (clientChildren == null) {
+                clientChildren = new ArrayList<>();
+                clientChildren.add(null);
+                clientChildren.addAll(remoteClients.get(remoteClientIndexOfParent).tabs);
+            }
+            return clientChildren.size();
         } else {
             final int historySize = historyCursor == null ? 0 : historyCursor.getCount();
             return remoteClients.size() + historySize + sectionHeaders.size();
         }
     }
 
     /**
      * Add only the SectionHeaders that have history items within their range to a SparseArray, where the
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.res.Configuration;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
@@ -77,16 +78,17 @@ public class CombinedHistoryPanel extend
 
     // String placeholders to mark formatting.
     private final static String FORMAT_S1 = "%1$s";
     private final static String FORMAT_S2 = "%2$s";
 
     private CombinedHistoryRecyclerView mRecyclerView;
     private CombinedHistoryAdapter mAdapter;
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
+    private int mSavedParentIndex = -1;
 
     private OnPanelLevelChangeListener.PanelLevel mPanelLevel = OnPanelLevelChangeListener.PanelLevel.PARENT;
     private Button mPanelFooterButton;
     // Reference to the View to display when there are no results.
     private View mEmptyView;
 
     public interface OnPanelLevelChangeListener {
         enum PanelLevel {
@@ -101,17 +103,17 @@ public class CombinedHistoryPanel extend
         return inflater.inflate(R.layout.home_combined_history_panel, container, false);
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         mRecyclerView = (CombinedHistoryRecyclerView) view.findViewById(R.id.combined_recycler_view);
-        mAdapter = new CombinedHistoryAdapter(getContext());
+        mAdapter = new CombinedHistoryAdapter(getContext(), mSavedParentIndex);
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setItemAnimator(new DefaultItemAnimator());
         mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext()));
         mRecyclerView.setOnHistoryClickedListener(mUrlOpenListener);
         mRecyclerView.setOnPanelLevelChangeListener(new OnLevelChangeListener());
         mRecyclerView.setHiddenClientsDialogBuilder(new HiddenClientsHelper());
         registerForContextMenu(mRecyclerView);
 
@@ -121,16 +123,27 @@ public class CombinedHistoryPanel extend
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         mCursorLoaderCallbacks = new CursorLoaderCallbacks();
     }
 
     @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        if (isVisible()) {
+            // The parent stack is saved just so that the folder state can be
+            // restored on rotation.
+            mSavedParentIndex = mAdapter.getParentIndex();
+        }
+    }
+
+    @Override
     protected void load() {
         getLoaderManager().initLoader(LOADER_ID_HISTORY, null, mCursorLoaderCallbacks);
         getLoaderManager().initLoader(LOADER_ID_REMOTE, null, mCursorLoaderCallbacks);
     }
 
     private static class RemoteTabsCursorLoader extends SimpleCursorLoader {
         private final GeckoProfile mProfile;
 
@@ -280,17 +293,17 @@ public class CombinedHistoryPanel extend
                             Telemetry.sendUIEvent(TelemetryContract.Event.SANITIZE, TelemetryContract.Method.BUTTON, "history");
                         }
                     });
 
                     dialogBuilder.show();
                     break;
 
                 case CHILD:
-                    final JSONArray tabUrls = ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).getCurrentChildTabs();
+                    final JSONArray tabUrls = mAdapter.getCurrentChildTabs();
                     if (tabUrls != null) {
                         final JSONObject message = new JSONObject();
                         try {
                             message.put("urls", tabUrls);
                             message.put("shouldNotifyTabsOpenedToJava", false);
                             GeckoAppShell.notifyObservers("Tabs:OpenMultiple", message.toString());
                         } catch (JSONException e) {
                             Log.e(LOGTAG, "Error making JSON message to open tabs");
@@ -420,17 +433,17 @@ public class CombinedHistoryPanel extend
         if (!(menuInfo instanceof RemoteTabsClientContextMenuInfo)) {
             return false;
         }
 
         final RemoteTabsClientContextMenuInfo info = (RemoteTabsClientContextMenuInfo) menuInfo;
 
         final int itemId = item.getItemId();
         if (itemId == R.id.home_remote_tabs_hide_client) {
-            ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).removeItem(info.position);
+            mAdapter.removeItem(info.position);
             return true;
         }
 
         return false;
     }
 
     interface DialogBuilder<E> {
         void createAndShowDialog(List<E> items);
@@ -447,17 +460,17 @@ public class CombinedHistoryPanel extend
             dialog.show(getActivity().getSupportFragmentManager(), "show-clients");
         }
 
 
     }
 
     @Override
     public void onClients(List<RemoteClient> clients) {
-        ((CombinedHistoryAdapter) mRecyclerView.getAdapter()).unhideClients(clients);
+        mAdapter.unhideClients(clients);
     }
 
     /**
      * Stores information regarding the creation of the context menu for a remote client.
      */
     protected static class RemoteTabsClientContextMenuInfo extends HomeContextMenuInfo {
         protected final RemoteClient client;