Niedzielski has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/227633

Change subject: ~50KiB
......................................................................

~50KiB

Change-Id: I5eacc8c07844253974cba6eb479bbcc8c5a2fa4f
---
M wikipedia/build.gradle
M wikipedia/proguard-rules.pro
M wikipedia/res/values/preference_keys.xml
M wikipedia/src/main/java/org/wikipedia/NavDrawerFragment.java
M wikipedia/src/main/java/org/wikipedia/Site.java
A wikipedia/src/main/java/org/wikipedia/data/GsonMarshaller.java
A wikipedia/src/main/java/org/wikipedia/data/GsonUnmarshaller.java
A wikipedia/src/main/java/org/wikipedia/data/TabUnmarshaller.java
M wikipedia/src/main/java/org/wikipedia/nearby/NearbyFragment.java
M wikipedia/src/main/java/org/wikipedia/page/HtmlPageLoadStrategy.java
M wikipedia/src/main/java/org/wikipedia/page/JsonPageLoadStrategy.java
M wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
M wikipedia/src/main/java/org/wikipedia/page/PageBackStackItem.java
M wikipedia/src/main/java/org/wikipedia/page/PageInfoDialog.java
M wikipedia/src/main/java/org/wikipedia/page/PageLoadStrategy.java
M wikipedia/src/main/java/org/wikipedia/page/PageProperties.java
M wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java
M 
wikipedia/src/main/java/org/wikipedia/page/bottomcontent/BottomContentHandler.java
M wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java
M wikipedia/src/main/java/org/wikipedia/search/SearchArticlesFragment.java
M wikipedia/src/main/java/org/wikipedia/settings/Prefs.java
21 files changed, 236 insertions(+), 166 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia 
refs/changes/33/227633/1

diff --git a/wikipedia/build.gradle b/wikipedia/build.gradle
index a950b19..d560a57 100644
--- a/wikipedia/build.gradle
+++ b/wikipedia/build.gradle
@@ -127,6 +127,7 @@
     compile 'com.android.support:appcompat-v7:22.2.1' // includes support-v4
     compile 'com.android.support:design:22.2.1'
     compile 'com.android.support:percent:22.2.0'
+    compile 'com.google.code.gson:gson:2.3.1'
     compile 'com.squareup.okhttp:okhttp-urlconnection:2.4.0'
     compile 'com.squareup.okhttp:okhttp:2.4.0'
     compile 'com.squareup:otto:1.3.6'
diff --git a/wikipedia/proguard-rules.pro b/wikipedia/proguard-rules.pro
index b0e4737..439f2f0 100644
--- a/wikipedia/proguard-rules.pro
+++ b/wikipedia/proguard-rules.pro
@@ -85,3 +85,21 @@
 # Our code:
 -keep class org.wikipedia.** {*;}
 -keep class org.mediawiki.api.json.** {*;}
+
+##---------------Begin: proguard configuration for Gson  ----------
+# 
https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg
+# Gson uses generic type information stored in a class file when working with 
fields. Proguard
+# removes such information by default, so configure it to keep all of it.
+#-keepattributes Signature (already specified)
+
+# For using GSON @Expose annotation (already specified)
+#-keepattributes *Annotation*
+
+# Gson specific classes
+-keep class sun.misc.Unsafe { *; }
+#-keep class com.google.gson.stream.** { *; }
+
+# Application classes that will be serialized/deserialized over Gson
+-keep class com.google.gson.examples.android.model.** { *; }
+
+##---------------End: proguard configuration for Gson  ----------
diff --git a/wikipedia/res/values/preference_keys.xml 
b/wikipedia/res/values/preference_keys.xml
index 2de5ee6..b0e674c 100644
--- a/wikipedia/res/values/preference_keys.xml
+++ b/wikipedia/res/values/preference_keys.xml
@@ -31,4 +31,5 @@
     <string 
name="preference_key_more_like_search_enabled">moreLikeSearchEnabled</string>
     <string 
name="preference_key_show_developer_settings">showDeveloperSettings</string>
     <string name="preference_key_last_run_time_format">%s-lastrun</string>
+    <string name="preference_key_tabs">tabs</string>
 </resources>
diff --git a/wikipedia/src/main/java/org/wikipedia/NavDrawerFragment.java 
b/wikipedia/src/main/java/org/wikipedia/NavDrawerFragment.java
index 8605550..1fef961 100644
--- a/wikipedia/src/main/java/org/wikipedia/NavDrawerFragment.java
+++ b/wikipedia/src/main/java/org/wikipedia/NavDrawerFragment.java
@@ -91,7 +91,8 @@
                             return;
                         }
                         HistoryEntry historyEntry = new HistoryEntry(title, 
HistoryEntry.SOURCE_RANDOM);
-                        ((PageActivity) getActivity()).displayNewPage(title, 
historyEntry, false, true);
+                        ((PageActivity) getActivity()).displayNewPage(title, 
historyEntry,
+                                PageActivity.TabPosition.CURRENT_TAB, true);
                     }
 
                     @Override
@@ -185,7 +186,7 @@
         Intent intent = new Intent();
         switch (view.getId()) {
             case R.id.nav_item_today:
-                ((PageActivity)getActivity()).displayMainPage();
+                ((PageActivity)getActivity()).displayMainPageInCurrentTab();
                 break;
             case R.id.nav_item_history:
                 ((PageActivity)getActivity()).pushFragment(new 
HistoryFragment());
@@ -233,7 +234,7 @@
                         if (!isAdded()) {
                             return;
                         }
-                        ((PageActivity)getActivity()).displayMainPage(true);
+                        
((PageActivity)getActivity()).displayMainPageInForegroundTab();
                         // and update any instances of our Featured Page 
widget, since it will
                         // change with the currently selected language.
                         Intent widgetIntent = new Intent(getActivity(), 
WidgetProviderFeaturedPage.class);
diff --git a/wikipedia/src/main/java/org/wikipedia/Site.java 
b/wikipedia/src/main/java/org/wikipedia/Site.java
index f2c58bc..baa2434 100644
--- a/wikipedia/src/main/java/org/wikipedia/Site.java
+++ b/wikipedia/src/main/java/org/wikipedia/Site.java
@@ -41,10 +41,6 @@
         return "/w/" + script;
     }
 
-    public String getResourceLoaderPath() {
-        return getScriptPath("load.php");
-    }
-
     public String getApiDomain() {
         return WikipediaApp.getInstance().getSslFallback() ? domain : 
urlToMobileSite(domain);
     }
diff --git a/wikipedia/src/main/java/org/wikipedia/data/GsonMarshaller.java 
b/wikipedia/src/main/java/org/wikipedia/data/GsonMarshaller.java
new file mode 100644
index 0000000..23bdf3d
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/data/GsonMarshaller.java
@@ -0,0 +1,13 @@
+package org.wikipedia.data;
+
+import com.google.gson.Gson;
+
+public final class GsonMarshaller {
+    private static final Gson DEFAULT_GSON = new Gson();
+
+    public static String marshal(Object object) {
+        return DEFAULT_GSON.toJson(object);
+    }
+
+    private GsonMarshaller() { }
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/data/GsonUnmarshaller.java 
b/wikipedia/src/main/java/org/wikipedia/data/GsonUnmarshaller.java
new file mode 100644
index 0000000..0f1f087
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/data/GsonUnmarshaller.java
@@ -0,0 +1,35 @@
+package org.wikipedia.data;
+
+import android.support.annotation.NonNull;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.Collection;
+
+public final class GsonUnmarshaller {
+    private static final Gson DEFAULT_GSON = new Gson();
+
+    /** @return Unmarshalled object. */
+    public static <T> T unmarshal(Class<T> clazz, @NonNull String json) {
+        return unmarshal(DEFAULT_GSON, clazz, json);
+    }
+
+    /** @return Unmarshalled collection of objects. */
+    public static <T extends Collection<?>> T unmarshal(TypeToken<T> 
typeToken, @NonNull String json) {
+        return unmarshal(DEFAULT_GSON, typeToken, json);
+    }
+
+    /** @return Unmarshalled object. */
+    public static <T> T unmarshal(@NonNull Gson gson, Class<T> clazz, @NonNull 
String json) {
+        return gson.fromJson(json, clazz);
+    }
+
+    /** @return Unmarshalled collection of objects. */
+    public static <T extends Collection<?>> T unmarshal(@NonNull Gson gson, 
TypeToken<T> typeToken, @NonNull String json) {
+        // From the manual: "Fairly hideous...  Unfortunately, no way to get 
around this in Java".
+        return gson.fromJson(json, typeToken.getType());
+    }
+
+    private GsonUnmarshaller() { }
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/data/TabUnmarshaller.java 
b/wikipedia/src/main/java/org/wikipedia/data/TabUnmarshaller.java
new file mode 100644
index 0000000..f66bb70
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/data/TabUnmarshaller.java
@@ -0,0 +1,17 @@
+package org.wikipedia.data;
+
+import com.google.gson.reflect.TypeToken;
+
+import org.wikipedia.page.tabs.Tab;
+
+import java.util.List;
+
+public final class TabUnmarshaller {
+    private static final TypeToken<List<Tab>> TYPE_TOKEN = new 
TypeToken<List<Tab>>() { };
+
+    public static List<Tab> unmarshal(String json) {
+        return GsonUnmarshaller.unmarshal(TYPE_TOKEN, json);
+    }
+
+    private TabUnmarshaller() { }
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/nearby/NearbyFragment.java 
b/wikipedia/src/main/java/org/wikipedia/nearby/NearbyFragment.java
index 95e0cac..2bf2e8c 100644
--- a/wikipedia/src/main/java/org/wikipedia/nearby/NearbyFragment.java
+++ b/wikipedia/src/main/java/org/wikipedia/nearby/NearbyFragment.java
@@ -237,12 +237,14 @@
 
         @Override
         public void onOpenLink(PageTitle title, HistoryEntry entry) {
-            ((PageActivity)getActivity()).displayNewPage(title, entry, false, 
false);
+            ((PageActivity)getActivity()).displayNewPage(title, entry,
+                    PageActivity.TabPosition.CURRENT_TAB, false);
         }
 
         @Override
         public void onOpenInNewTab(PageTitle title, HistoryEntry entry) {
-            ((PageActivity)getActivity()).displayNewPage(title, entry, true, 
false);
+            ((PageActivity)getActivity()).displayNewPage(title, entry,
+                    PageActivity.TabPosition.NEW_TAB_BACKGROUND, false);
         }
     };
 
diff --git 
a/wikipedia/src/main/java/org/wikipedia/page/HtmlPageLoadStrategy.java 
b/wikipedia/src/main/java/org/wikipedia/page/HtmlPageLoadStrategy.java
index c167016..b416f68 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/HtmlPageLoadStrategy.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/HtmlPageLoadStrategy.java
@@ -14,7 +14,6 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.http.SslError;
-import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebResourceRequest;
@@ -23,6 +22,7 @@
 import android.webkit.WebViewClient;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Our new page load strategy, which loads the page via webView#loadUrl.
@@ -93,7 +93,7 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
+    public void onActivityCreated(@NonNull List<PageBackStackItem> backStack) {
         setupSpecificMessageHandlers();
 
         webView.setWebViewClient(new WebViewClient() {
@@ -195,11 +195,6 @@
     }
 
     @Override
-    public void onSaveInstanceState(Bundle outState) {
-        // nothing to do here
-    }
-
-    @Override
     public void backFromEditing(Intent data) {
         // Retrieve section ID from intent, and find correct section, so we 
know where to scroll to
 //        sectionTargetFromIntent = 
data.getIntExtra(EditSectionActivity.EXTRA_SECTION_ID, 0);
@@ -266,7 +261,7 @@
     }
 
     @Override
-    public void setBackStack(ArrayList<PageBackStackItem> backStack) {
+    public void setBackStack(@NonNull List<PageBackStackItem> backStack) {
         // TODO: implement switching of backstacks from multiple tabs.
     }
 
diff --git 
a/wikipedia/src/main/java/org/wikipedia/page/JsonPageLoadStrategy.java 
b/wikipedia/src/main/java/org/wikipedia/page/JsonPageLoadStrategy.java
index af704a2..ca51ba6 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/JsonPageLoadStrategy.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/JsonPageLoadStrategy.java
@@ -34,7 +34,6 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Build;
-import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.util.Log;
 import android.view.View;
@@ -74,7 +73,7 @@
      * Since the list consists of Parcelable objects, it can be saved and 
restored from the
      * savedInstanceState of the fragment.
      */
-    private ArrayList<PageBackStackItem> backStack;
+    @NonNull private List<PageBackStackItem> backStack;
 
     /**
      * Sequence number to maintain synchronization when loading page content 
asynchronously
@@ -114,7 +113,7 @@
     }
 
     @Override
-    public void setBackStack(@NonNull ArrayList<PageBackStackItem> backStack) {
+    public void setBackStack(@NonNull List<PageBackStackItem> backStack) {
         this.backStack = backStack;
     }
 
@@ -135,18 +134,12 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
+    public void onActivityCreated(@NonNull List<PageBackStackItem> backStack) {
         setupSpecificMessageHandlers();
 
         currentSequenceNum = 0;
 
-        if (savedInstanceState != null) {
-            ArrayList<PageBackStackItem> tmpBackStack
-                    = savedInstanceState.getParcelableArrayList("backStack");
-            if (tmpBackStack != null) { // avoid setting backStack to null
-                backStack = tmpBackStack;
-            }
-        }
+        this.backStack = backStack;
 
         // if we already have pages in the backstack (whether it's from 
savedInstanceState, or
         // from being stored in the activity's fragment backstack), then load 
the topmost page
@@ -218,13 +211,6 @@
         bottomContentHandler = new BottomContentHandler(fragment, bridge, 
webView,
                 fragment.getLinkHandler(),
                 (ViewGroup) 
fragment.getView().findViewById(R.id.bottom_content_container));
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        // update the topmost entry in the back stack
-        updateCurrentBackStackItem();
-        outState.putParcelableArrayList("backStack", backStack);
     }
 
     @Override
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
index fc365fa..cf5e594 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
@@ -64,6 +64,11 @@
 import android.widget.TextView;
 
 public class PageActivity extends ThemedActionBarActivity {
+    public enum TabPosition {
+        CURRENT_TAB,
+        NEW_TAB_BACKGROUND,
+        NEW_TAB_FOREGROUND
+    }
     public static final String ACTION_PAGE_FOR_TITLE = 
"org.wikipedia.page_for_title";
     public static final String EXTRA_PAGETITLE = "org.wikipedia.pagetitle";
     public static final String EXTRA_HISTORYENTRY  = 
"org.wikipedia.history.historyentry";
@@ -246,7 +251,7 @@
 
         if (languageChanged) {
             app.resetSite();
-            displayMainPage();
+            displayMainPageInForegroundTab();
         }
 
         // If we're coming back from a Theme change, we'll need to "restore" 
our state based on
@@ -269,7 +274,7 @@
                 //multiple various exceptions may be thrown in the above few 
lines, so just catch all.
                 Log.e("PageActivity", "Error while instantiating fragment.", 
e);
                 //don't let the user see a blank screen, so just request the 
main page...
-                displayMainPage();
+                displayMainPageInCurrentTab();
             }
         } else if (savedInstanceState == null) {
             // if there's no savedInstanceState, and we're not coming back 
from a Theme change,
@@ -407,16 +412,11 @@
             new WidgetsFunnel(app).logSearchWidgetTap();
             openSearch();
         } else if (intent.hasExtra(EXTRA_FEATURED_ARTICLE_FROM_WIDGET)) {
-            displayMainPage();
-
             // Log that the user tapped on the featured article widget
             // Instantiate the funnel anonymously to save on memory overhead
             new WidgetsFunnel(app).logFeaturedArticleWidgetTap();
-        } else {
-            // Unrecognized Intent was handled, or the user opened the app by 
tapping on the icon.
-            // Let us load the main page!
-            displayMainPage();
         }
+        displayMainPageIfNoTabs();
     }
 
     private void handleShareIntent(Intent intent) {
@@ -461,10 +461,7 @@
      * @return True if currently searching, false otherwise.
      */
     public boolean isSearching() {
-        if (searchFragment == null) {
-            return false;
-        }
-        return searchFragment.isSearchActive();
+        return searchFragment != null && searchFragment.isSearchActive();
     }
 
     /**
@@ -548,7 +545,14 @@
      * @param entry HistoryEntry associated with this page.
      */
     public void displayNewPage(PageTitle title, HistoryEntry entry) {
-        displayNewPage(title, entry, false, false);
+        displayNewPage(title, entry, TabPosition.CURRENT_TAB, false);
+    }
+
+    public void displayNewPage(PageTitle title,
+                               HistoryEntry entry,
+                               TabPosition position,
+                               boolean allowStateLoss) {
+        displayNewPage(title, entry, position, allowStateLoss, false);
     }
 
     /**
@@ -556,11 +560,16 @@
      * fragment manager. Useful for when this function is called from an 
AsyncTask result.
      * @param title Title of the page to load.
      * @param entry HistoryEntry associated with this page.
-     * @param inNewTab Whether to open this page in a new tab.
+     * @param position Whether to open this page in the current tab, a new 
background tab, or new
+     *                 foreground tab.
      * @param allowStateLoss Whether to allow state loss.
+     * @param mustBeEmpty If true, and a tab exists already, do nothing.
      */
-    public void displayNewPage(final PageTitle title, final HistoryEntry entry,
-                               final boolean inNewTab, boolean allowStateLoss) 
{
+    public void displayNewPage(final PageTitle title,
+                               final HistoryEntry entry,
+                               final TabPosition position,
+                               boolean allowStateLoss,
+                               final boolean mustBeEmpty) {
         ACRA.getErrorReporter().putCustomData("api", 
title.getSite().getApiDomain());
         ACRA.getErrorReporter().putCustomData("title", title.toString());
 
@@ -584,7 +593,7 @@
                 if (!frag.getCurrentTab().getBackStack().isEmpty()
                         && frag.getCurrentTab().getBackStack()
                         .get(frag.getCurrentTab().getBackStack().size() - 
1).getTitle()
-                        .equals(title)) {
+                        .equals(title) || mustBeEmpty && 
!frag.getCurrentTab().getBackStack().isEmpty()) {
                     //if we have a section to scroll to, then pass it to the 
fragment
                     if (!TextUtils.isEmpty(title.getFragment())) {
                         frag.scrollToSection(title.getFragment());
@@ -592,32 +601,44 @@
                     return;
                 }
                 frag.closeFindInPage();
-                if (inNewTab) {
-                    frag.openInNewTabFromMenu(title, entry);
-                } else {
+                if (position == TabPosition.CURRENT_TAB) {
                     frag.displayNewPage(title, entry, false, true);
+                } else if (position == TabPosition.NEW_TAB_BACKGROUND) {
+                    frag.openInNewBackgroundTabFromMenu(title, entry);
+                } else {
+                    frag.openInNewForegroundTabFromMenu(title, entry);
                 }
                 app.getSessionFunnel().pageViewed(entry);
             }
         });
     }
 
-    /**
-     * Go directly to the Main Page of the current Wiki.
-     */
-    public void displayMainPage() {
-        displayMainPage(false);
+    public void displayMainPageInCurrentTab() {
+        displayMainPage(false, TabPosition.CURRENT_TAB, false);
+    }
+
+    public void displayMainPageInForegroundTab() {
+        displayMainPage(true, TabPosition.NEW_TAB_FOREGROUND, false);
+    }
+
+    public void displayMainPageIfNoTabs() {
+        displayMainPage(false, TabPosition.CURRENT_TAB, true);
     }
 
     /**
      * Go directly to the Main Page of the current Wiki, optionally allowing 
state loss of the
      * fragment manager. Useful for when this function is called from an 
AsyncTask result.
-     * @param allowStateLoss Whether to allow state loss.
+     * @param allowStateLoss Allows the {@link 
android.support.v4.app.FragmentManager} commit to be
+     *                       executed after an activity's state is saved.  
This is dangerous because
+     *                       the commit can be lost if the activity needs to 
later be restored from
+     *                       its state, so this should only be used for cases 
where it is okay for
+     *                       the UI state to change unexpectedly on the user.
+     * @param mustBeEmpty If true, and a tab exists already, do nothing.
      */
-    public void displayMainPage(boolean allowStateLoss) {
+    public void displayMainPage(boolean allowStateLoss, TabPosition position, 
boolean mustBeEmpty) {
         PageTitle title = new 
PageTitle(MainPageNameData.valueFor(app.getAppOrSystemLanguageCode()), 
app.getPrimarySite());
         HistoryEntry historyEntry = new HistoryEntry(title, 
HistoryEntry.SOURCE_MAIN_PAGE);
-        displayNewPage(title, historyEntry, false, allowStateLoss);
+        displayNewPage(title, historyEntry, position, allowStateLoss, 
mustBeEmpty);
     }
 
     public void showThemeChooser() {
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageBackStackItem.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageBackStackItem.java
index 59cf0af..bd7565b 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageBackStackItem.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageBackStackItem.java
@@ -1,10 +1,8 @@
 package org.wikipedia.page;
 
 import org.wikipedia.history.HistoryEntry;
-import android.os.Parcel;
-import android.os.Parcelable;
 
-public class PageBackStackItem implements Parcelable {
+public class PageBackStackItem {
     private final PageTitle title;
     public PageTitle getTitle() {
         return title;
@@ -27,33 +25,4 @@
         this.title = title;
         this.historyEntry = historyEntry;
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(title, flags);
-        dest.writeParcelable(historyEntry, flags);
-        dest.writeInt(scrollY);
-    }
-
-    private PageBackStackItem(Parcel in) {
-        title = in.readParcelable(PageTitle.class.getClassLoader());
-        historyEntry = in.readParcelable(HistoryEntry.class.getClassLoader());
-        scrollY = in.readInt();
-    }
-
-    public static final Parcelable.Creator<PageBackStackItem> CREATOR
-            = new Parcelable.Creator<PageBackStackItem>() {
-        public PageBackStackItem createFromParcel(Parcel in) {
-            return new PageBackStackItem(in);
-        }
-
-        public PageBackStackItem[] newArray(int size) {
-            return new PageBackStackItem[size];
-        }
-    };
-}
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageInfoDialog.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageInfoDialog.java
index e8b67bc..ec83a0e 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageInfoDialog.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageInfoDialog.java
@@ -98,7 +98,7 @@
         @Override
         public void onOpenInNewTab(PageTitle title, HistoryEntry entry) {
             dismiss();
-            activity.displayNewPage(title, entry, true, false);
+            activity.displayNewPage(title, entry, 
PageActivity.TabPosition.NEW_TAB_BACKGROUND, false);
         }
     };
 
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageLoadStrategy.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageLoadStrategy.java
index 0d91268..a15f975 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageLoadStrategy.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageLoadStrategy.java
@@ -8,8 +8,9 @@
 import org.wikipedia.views.SwipeRefreshLayoutWithScroll;
 
 import android.content.Intent;
-import android.os.Bundle;
-import java.util.ArrayList;
+import android.support.annotation.NonNull;
+
+import java.util.List;
 
 /**
  * Defines interaction between PageViewFragmentInternal and an implementation 
that loads a page
@@ -21,9 +22,7 @@
                CommunicationBridge bridge, SearchBarHideHandler 
searchBarHideHandler,
                LeadImagesHandler leadImagesHandler);
 
-    void onActivityCreated(Bundle savedInstanceState);
-
-    void onSaveInstanceState(Bundle outState);
+    void onActivityCreated(@NonNull List<PageBackStackItem> backStack);
 
     void backFromEditing(Intent data);
 
@@ -41,7 +40,7 @@
 
     void setEditHandler(EditHandler editHandler);
 
-    void setBackStack(ArrayList<PageBackStackItem> backStack);
+    void setBackStack(@NonNull List<PageBackStackItem> backStack);
 
     void updateCurrentBackStackItem();
 
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageProperties.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageProperties.java
index 90f32c0..4762af7 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageProperties.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageProperties.java
@@ -248,7 +248,6 @@
                 json.put("image", imageObject);
             }
         } catch (JSONException e) {
-            // Goddamn it Java
             throw new RuntimeException(e);
         }
 
diff --git 
a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java
index ae8710b..9864877 100755
--- a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragmentInternal.java
@@ -68,6 +68,7 @@
 import com.appenguin.onboarding.ToolTip;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import javax.net.ssl.SSLException;
@@ -75,9 +76,6 @@
 // TODO: USE ACRA.getErrorReporter().handleSilentException() if we move to 
automated crash reporting?
 
 public class PageViewFragmentInternal extends Fragment implements 
BackPressedHandler {
-    private static final String TAG = "PageViewFragment";
-    private static final String TAB_LIST_KEY = "tabList";
-
     public static final int SUBSTATE_NONE = 0;
     public static final int SUBSTATE_PAGE_SAVED = 1;
     public static final int SUBSTATE_SAVED_PAGE_LOADED = 2;
@@ -94,7 +92,7 @@
      * savedInstanceState of the fragment.
      */
     @NonNull
-    private ArrayList<Tab> tabList = new ArrayList<>();
+    private final List<Tab> tabList = new ArrayList<>();
 
     @NonNull
     private TabFunnel tabFunnel = new TabFunnel();
@@ -192,6 +190,8 @@
         } else {
             pageLoadStrategy = new JsonPageLoadStrategy();
         }
+
+        initTabs();
     }
 
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -233,15 +233,6 @@
         super.onActivityCreated(savedInstanceState);
         setHasOptionsMenu(true);
         connectionIssueFunnel = new ConnectionIssueFunnel(app);
-
-        if (savedInstanceState != null) {
-            //noinspection ConstantConditions
-            tabList = savedInstanceState.getParcelableArrayList(TAB_LIST_KEY);
-        }
-
-        if (tabList.isEmpty()) {
-            tabList.add(new Tab());
-        }
 
         updateFontSize();
 
@@ -346,9 +337,8 @@
                 contextMenuListener);
 
         pageLoadStrategy.setup(model, this, refreshView, webView, bridge, 
searchBarHideHandler,
-                               leadImagesHandler);
-        pageLoadStrategy.setBackStack(getCurrentTab().getBackStack());
-        pageLoadStrategy.onActivityCreated(savedInstanceState);
+                leadImagesHandler);
+        pageLoadStrategy.onActivityCreated(getCurrentTab().getBackStack());
     }
 
     private void initWebViewListeners() {
@@ -407,7 +397,8 @@
 
         @Override
         public void onOpenInNewTab(PageTitle title, HistoryEntry entry) {
-            ((PageActivity) getActivity()).displayNewPage(title, entry, true, 
false);
+            ((PageActivity) getActivity()).displayNewPage(title, entry,
+                    PageActivity.TabPosition.NEW_TAB_BACKGROUND, false);
         }
     };
 
@@ -471,7 +462,7 @@
             // just load the main page into a new tab...
             PageTitle newTitle = new 
PageTitle(MainPageNameData.valueFor(app.getAppLanguageCode()), 
app.getPrimarySite());
             HistoryEntry newEntry = new HistoryEntry(newTitle, 
HistoryEntry.SOURCE_INTERNAL_LINK);
-            openInNewTab(newTitle, newEntry);
+            openInNewBackgroundTab(newTitle, newEntry);
             tabFunnel.logCreateNew(tabList.size());
         }
 
@@ -496,10 +487,9 @@
     };
 
     @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        pageLoadStrategy.onSaveInstanceState(outState);
-        outState.putParcelableArrayList(TAB_LIST_KEY, tabList);
+    public void onPause() {
+        super.onPause();
+        Prefs.setTabs(tabList);
     }
 
     @Override
@@ -516,8 +506,19 @@
         tabsProvider.invalidate();
     }
 
-    public void openInNewTabFromMenu(PageTitle title, HistoryEntry entry) {
-        openInNewTab(title, entry);
+    public void openInNewBackgroundTabFromMenu(PageTitle title, HistoryEntry 
entry) {
+        openInNewTabFromMenu(title, entry, getBackgroundTabPosition());
+    }
+
+    public void openInNewForegroundTabFromMenu(PageTitle title, HistoryEntry 
entry) {
+        openInNewTabFromMenu(title, entry, getForegroundTabPosition());
+        displayNewPage(title, entry, false, false);
+    }
+
+    public void openInNewTabFromMenu(PageTitle title,
+                                     HistoryEntry entry,
+                                     int position) {
+        openInNewTab(title, entry, position);
         tabFunnel.logOpenInNew(tabList.size());
     }
 
@@ -587,15 +588,27 @@
         webView.getSettings().setDefaultFontSize((int) 
app.getFontSize(getActivity().getWindow()));
     }
 
-    private void openInNewTab(PageTitle title, HistoryEntry entry) {
+    private void openInNewBackgroundTab(PageTitle title, HistoryEntry entry) {
+        openInNewTab(title, entry, getBackgroundTabPosition());
+    }
+
+    private void openInNewTab(PageTitle title, HistoryEntry entry, int 
position) {
         // create a new tab
         Tab tab = new Tab();
         // put this tab behind the current tab
-        tabList.add(Math.max(0, tabList.size() - 1), tab);
+        tabList.add(position, tab);
         // add the requested page to its backstack
         tab.getBackStack().add(new PageBackStackItem(title, entry));
         // and... that should be it.
         tabsProvider.showAndHideTabs();
+    }
+
+    private int getBackgroundTabPosition() {
+        return Math.max(0, getForegroundTabPosition() - 1);
+    }
+
+    private int getForegroundTabPosition() {
+        return tabList.size();
     }
 
     private void setupMessageHandlers() {
@@ -1064,4 +1077,14 @@
             }
         }, TimeUnit.SECONDS.toMillis(1));
     }
+
+    private void initTabs() {
+        if (Prefs.hasTabs()) {
+            tabList.addAll(Prefs.getTabs());
+        }
+
+        if (tabList.isEmpty()) {
+            tabList.add(new Tab());
+        }
+    }
 }
diff --git 
a/wikipedia/src/main/java/org/wikipedia/page/bottomcontent/BottomContentHandler.java
 
b/wikipedia/src/main/java/org/wikipedia/page/bottomcontent/BottomContentHandler.java
index 054b505..aa7c352 100644
--- 
a/wikipedia/src/main/java/org/wikipedia/page/bottomcontent/BottomContentHandler.java
+++ 
b/wikipedia/src/main/java/org/wikipedia/page/bottomcontent/BottomContentHandler.java
@@ -382,7 +382,7 @@
 
         @Override
         public void onOpenInNewTab(PageTitle title, HistoryEntry entry) {
-            activity.displayNewPage(title, entry, true, false);
+            activity.displayNewPage(title, entry, 
PageActivity.TabPosition.NEW_TAB_BACKGROUND, false);
             funnel.logSuggestionClicked(pageTitle, 
readMoreItems.getPageTitles(), lastPosition);
         }
     };
diff --git a/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java 
b/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java
index 00af63a..818f586 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/tabs/Tab.java
@@ -1,45 +1,17 @@
 package org.wikipedia.page.tabs;
 
 import org.wikipedia.page.PageBackStackItem;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.support.annotation.NonNull;
 import java.util.ArrayList;
+import java.util.List;
 
-public class Tab implements Parcelable {
-    private final ArrayList<PageBackStackItem> backStack;
 
-    public Tab() {
-        backStack = new ArrayList<>();
-    }
+
+public class Tab {
+    @NonNull private final List<PageBackStackItem> backStack = new 
ArrayList<>();
 
     @NonNull
-    public ArrayList<PageBackStackItem> getBackStack() {
+    public List<PageBackStackItem> getBackStack() {
         return backStack;
     }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeList(backStack);
-    }
-
-    private Tab(Parcel in) {
-        backStack = (ArrayList<PageBackStackItem>) 
in.readArrayList(Tab.class.getClassLoader());
-    }
-
-    public static final Parcelable.Creator<Tab> CREATOR
-            = new Parcelable.Creator<Tab>() {
-        public Tab createFromParcel(Parcel in) {
-            return new Tab(in);
-        }
-
-        public Tab[] newArray(int size) {
-            return new Tab[size];
-        }
-    };
 }
diff --git 
a/wikipedia/src/main/java/org/wikipedia/search/SearchArticlesFragment.java 
b/wikipedia/src/main/java/org/wikipedia/search/SearchArticlesFragment.java
index e6fa4a1..e81cee1 100644
--- a/wikipedia/src/main/java/org/wikipedia/search/SearchArticlesFragment.java
+++ b/wikipedia/src/main/java/org/wikipedia/search/SearchArticlesFragment.java
@@ -413,7 +413,9 @@
         // selected a page to navigate to.
         launchedFromWidget = false;
         closeSearch();
-        ((PageActivity)getActivity()).displayNewPage(title, historyEntry, 
inNewTab, false);
+        ((PageActivity)getActivity()).displayNewPage(title, historyEntry, 
inNewTab
+                ? PageActivity.TabPosition.NEW_TAB_BACKGROUND
+                : PageActivity.TabPosition.CURRENT_TAB, false);
     }
 
     private void addRecentSearch(String title) {
diff --git a/wikipedia/src/main/java/org/wikipedia/settings/Prefs.java 
b/wikipedia/src/main/java/org/wikipedia/settings/Prefs.java
index 963d87c..551d0df 100644
--- a/wikipedia/src/main/java/org/wikipedia/settings/Prefs.java
+++ b/wikipedia/src/main/java/org/wikipedia/settings/Prefs.java
@@ -5,7 +5,13 @@
 
 import org.wikipedia.R;
 import org.wikipedia.WikipediaApp;
+import org.wikipedia.data.GsonMarshaller;
+import org.wikipedia.data.TabUnmarshaller;
+import org.wikipedia.page.tabs.Tab;
 import org.wikipedia.theme.Theme;
+
+import java.util.Collections;
+import java.util.List;
 
 import static org.wikipedia.settings.PrefsIoUtil.contains;
 import static org.wikipedia.settings.PrefsIoUtil.getBoolean;
@@ -203,6 +209,21 @@
         setString(R.string.preference_key_remote_config, json);
     }
 
+    public static void setTabs(@NonNull List<Tab> tabs) {
+        setString(R.string.preference_key_tabs, GsonMarshaller.marshal(tabs));
+    }
+
+    @NonNull
+    public static List<Tab> getTabs() {
+        return hasTabs()
+                ? 
TabUnmarshaller.unmarshal(getString(R.string.preference_key_tabs, "{}"))
+                : Collections.<Tab>emptyList();
+    }
+
+    public static boolean hasTabs() {
+        return contains(R.string.preference_key_tabs);
+    }
+
     public static int getTextSizeMultiplier() {
         return getInt(R.string.preference_key_text_size_multiplier, 0);
     }
@@ -231,7 +252,6 @@
         setBoolean(R.string.preference_key_exp_json_page_load, enabled);
     }
 
-    @NonNull
     public static long getLastRunTime(@NonNull String task) {
         return getLong(getLastRunTimeKey(task), 0);
     }

-- 
To view, visit https://gerrit.wikimedia.org/r/227633
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I5eacc8c07844253974cba6eb479bbcc8c5a2fa4f
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <sniedziel...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to