jenkins-bot has submitted this change and it was merged.

Change subject: Keep only a limited number of WebViews in memory.
......................................................................


Keep only a limited number of WebViews in memory.

(scroll position to be implemented in next patch)

Bug: 64450

Change-Id: I768e2dcae18924d78253fad66bfaa83b8447d94a
---
M wikipedia/res/layout/activity_main.xml
A 
wikipedia/src/main/java/android/support/v4/app/FixedFragmentStatePagerAdapter.java
A wikipedia/src/main/java/org/wikipedia/page/BackStack.java
A wikipedia/src/main/java/org/wikipedia/page/BackStackItem.java
M wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
A wikipedia/src/main/java/org/wikipedia/page/PageFragmentAdapter.java
A wikipedia/src/main/java/org/wikipedia/page/PageFragmentPager.java
M wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
8 files changed, 525 insertions(+), 50 deletions(-)

Approvals:
  Yuvipanda: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/wikipedia/res/layout/activity_main.xml 
b/wikipedia/res/layout/activity_main.xml
index 7a767b2..4a70937 100644
--- a/wikipedia/res/layout/activity_main.xml
+++ b/wikipedia/res/layout/activity_main.xml
@@ -7,11 +7,10 @@
             android:saveEnabled="false"
             android:layout_height="match_parent">
         <!-- The main content view -->
-        <FrameLayout
-                android:id="@+id/content_frame"
+        <org.wikipedia.page.PageFragmentPager
+                android:id="@+id/content_pager"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent">
-        </FrameLayout>
+                android:layout_height="match_parent" />
         <!-- The navigation drawer -->
         <!-- Don't set marginTop here, it somehow also affects marginBottom. 
wtf?! -->
         <fragment android:layout_width="288dp" 
android:layout_height="match_parent"
diff --git 
a/wikipedia/src/main/java/android/support/v4/app/FixedFragmentStatePagerAdapter.java
 
b/wikipedia/src/main/java/android/support/v4/app/FixedFragmentStatePagerAdapter.java
new file mode 100644
index 0000000..eebd065
--- /dev/null
+++ 
b/wikipedia/src/main/java/android/support/v4/app/FixedFragmentStatePagerAdapter.java
@@ -0,0 +1,28 @@
+package android.support.v4.app;
+
+import android.os.Bundle;
+import android.view.ViewGroup;
+
+/**
+ * TODO: Remove this class when Google updates the Support library.
+ * This solves an intermittent crash when using FragmentStatePagerAdapter.
+ *
+ * Android bug: https://code.google.com/p/android/issues/detail?id=37484
+ */
+public abstract class FixedFragmentStatePagerAdapter extends 
FragmentStatePagerAdapter {
+
+    public FixedFragmentStatePagerAdapter(FragmentManager fm) {
+        super(fm);
+    }
+
+    @Override
+    public Object instantiateItem(ViewGroup container, int position) {
+        Fragment f = (Fragment) super.instantiateItem(container, position);
+        Bundle savedFragmentState = f.mSavedFragmentState;
+        if (savedFragmentState != null) {
+            savedFragmentState.setClassLoader(f.getClass().getClassLoader());
+        }
+        return f;
+    }
+
+}
diff --git a/wikipedia/src/main/java/org/wikipedia/page/BackStack.java 
b/wikipedia/src/main/java/org/wikipedia/page/BackStack.java
new file mode 100644
index 0000000..fb36074
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/page/BackStack.java
@@ -0,0 +1,47 @@
+package org.wikipedia.page;
+
+import android.os.*;
+import java.util.*;
+
+public class BackStack implements Parcelable {
+
+    private ArrayList<BackStackItem> backStack;
+
+    public ArrayList<BackStackItem> getStack() {
+        return backStack;
+    }
+
+    public int size() {
+        return backStack.size();
+    }
+
+    public BackStack() {
+        backStack = new ArrayList<BackStackItem>();
+    }
+
+    public BackStack(Parcel in) {
+        backStack = in.readArrayList(BackStackItem.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeList(backStack);
+    }
+
+    public static final Parcelable.Creator<BackStack> CREATOR
+            = new Parcelable.Creator<BackStack>() {
+        public BackStack createFromParcel(Parcel in) {
+            return new BackStack(in);
+        }
+
+        public BackStack[] newArray(int size) {
+            return new BackStack[size];
+        }
+    };
+
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/page/BackStackItem.java 
b/wikipedia/src/main/java/org/wikipedia/page/BackStackItem.java
new file mode 100644
index 0000000..12c99f1
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/page/BackStackItem.java
@@ -0,0 +1,47 @@
+package org.wikipedia.page;
+
+import android.os.*;
+import org.wikipedia.*;
+import org.wikipedia.history.*;
+
+public class BackStackItem implements Parcelable {
+    public final PageTitle title;
+    public final HistoryEntry historyEntry;
+    public final int scrollPosition;
+
+    public BackStackItem(PageTitle title, HistoryEntry historyEntry, int 
scrollPosition) {
+        this.title = title;
+        this.historyEntry = historyEntry;
+        this.scrollPosition = scrollPosition;
+    }
+
+    public BackStackItem(Parcel in) {
+        title = in.readParcelable(PageTitle.class.getClassLoader());
+        historyEntry = in.readParcelable(HistoryEntry.class.getClassLoader());
+        scrollPosition = in.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(title, flags);
+        parcel.writeParcelable(historyEntry, flags);
+        parcel.writeInt(scrollPosition);
+    }
+
+    public static final Parcelable.Creator<BackStackItem> CREATOR
+            = new Parcelable.Creator<BackStackItem>() {
+        public BackStackItem createFromParcel(Parcel in) {
+            return new BackStackItem(in);
+        }
+
+        public BackStackItem[] newArray(int size) {
+            return new BackStackItem[size];
+        }
+    };
+
+}
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
index a7636d6..f2a6cbc 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageActivity.java
@@ -1,11 +1,12 @@
 package org.wikipedia.page;
 
-import android.app.*;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
 import android.content.*;
 import android.net.*;
 import android.os.*;
 import android.preference.*;
-import android.support.v4.widget.*;
+import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.*;
 import android.util.*;
 import android.view.*;
@@ -13,9 +14,11 @@
 import de.keyboardsurfer.android.widget.crouton.*;
 import org.wikipedia.*;
 import org.wikipedia.analytics.*;
+import org.wikipedia.concurrency.SaneAsyncTask;
 import org.wikipedia.events.*;
 import org.wikipedia.history.*;
 import org.wikipedia.interlanguage.*;
+import org.wikipedia.pageimages.PageImageSaveTask;
 import org.wikipedia.recurring.*;
 import org.wikipedia.search.*;
 import org.wikipedia.settings.*;
@@ -31,10 +34,22 @@
     private Bus bus;
     private WikipediaApp app;
 
-    private SearchArticlesFragment searchAriclesFragment;
+    private SearchArticlesFragment searchArticlesFragment;
     private DrawerLayout drawerLayout;
 
+    /**
+     * Container that will hold our WebViews, and animate between them.
+     */
+    private PageFragmentPager fragmentPager;
+
     private PageViewFragment curPageFragment;
+
+    private PageFragmentAdapter fragmentAdapter;
+
+    /**
+     * Lightweight back-stack of history items
+     */
+    private BackStack backStack;
 
     private boolean pausedStateOfZero;
     private String pausedXcsOfZero;
@@ -54,9 +69,11 @@
         if (savedInstanceState != null) {
             pausedStateOfZero = 
savedInstanceState.getBoolean("pausedStateOfZero");
             pausedXcsOfZero = savedInstanceState.getString("pausedXcsOfZero");
-            if (savedInstanceState.containsKey("curPageFragment")) {
-                curPageFragment = (PageViewFragment) 
getSupportFragmentManager().getFragment(savedInstanceState, "curPageFragment");
+            if (savedInstanceState.containsKey("backStack")) {
+                backStack = savedInstanceState.getParcelable("backStack");
             }
+        } else {
+            backStack = new BackStack();
         }
 
         bus = app.getBus();
@@ -64,10 +81,22 @@
 
         readingActionFunnel = new ReadingActionFunnel(app);
 
-        searchAriclesFragment = (SearchArticlesFragment) 
getSupportFragmentManager().findFragmentById(R.id.search_fragment);
+        searchArticlesFragment = (SearchArticlesFragment) 
getSupportFragmentManager().findFragmentById(R.id.search_fragment);
         drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
 
-        searchAriclesFragment.setDrawerLayout(drawerLayout);
+        searchArticlesFragment.setDrawerLayout(drawerLayout);
+
+        fragmentPager = (PageFragmentPager) findViewById(R.id.content_pager);
+        // disable the default swipe motion to flip pages (we'll be doing it 
programmatically)
+        fragmentPager.setPagingEnabled(false);
+
+        fragmentAdapter = new PageFragmentAdapter(getSupportFragmentManager(), 
backStack);
+        fragmentPager.setAdapter(fragmentAdapter);
+
+        // Set the maximum number of fragments that will be kept in memory.
+        // Old Fragments will be automatically destroyed, but their state will 
be saved,
+        // so when the user goes back, they will be recreated.
+        fragmentPager.setOffscreenPageLimit(calculateMaxFragments());
 
         if (savedInstanceState == null) {
             // Don't do this if we are just rotating the phone
@@ -92,7 +121,25 @@
         new RecurringTasksExecutor(this).run();
     }
 
-    private void displayNewPage(PageTitle title, HistoryEntry entry) {
+    private int calculateMaxFragments() {
+        // calculate the maximum number of WebViews to keep in memory, based 
on VM size
+        ActivityManager activityManager = 
(ActivityManager)getApplicationContext().getSystemService(ACTIVITY_SERVICE);
+        int memMegs = activityManager.getMemoryClass();
+        // allow up to 7MB for the app itself, 3 MB for spikes by WebView 
allocations,
+        // and 2 MB for each WebView stored in memory.
+        int maxFragments = (memMegs - 7 - 3) / 2;
+        if (maxFragments <= 0) {
+            // make sure there's at least one, for really low-memory devices.
+            maxFragments = 1;
+        } else if (maxFragments > 6) {
+            // more than this will probably break rendering.
+            maxFragments = 6;
+        }
+        Log.d("PageActivity", "Maximum Fragments in memory: " + maxFragments);
+        return maxFragments;
+    }
+
+    private void displayNewPage(final PageTitle title, final HistoryEntry 
entry) {
         readingActionFunnel.logSomethingHappened(title.getSite());
         if (drawerLayout.isDrawerOpen(Gravity.START)) {
             drawerLayout.closeDrawer(Gravity.START);
@@ -101,17 +148,56 @@
             Utils.visitInExternalBrowser(this, 
Uri.parse(title.getMobileUri()));
             return;
         }
-        PageViewFragment pageFragment = new PageViewFragment(title, entry, 
R.id.search_fragment);
-        getSupportFragmentManager().beginTransaction()
-                .setCustomAnimations(R.anim.slide_in_right, 
R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right)
-                .add(R.id.content_frame, pageFragment, title.getCanonicalUri())
-                .addToBackStack(title.getCanonicalUri())
-                .commit();
 
-        if (curPageFragment != null) {
-            curPageFragment.hide();
+        // Add history entry now
+        new HistorySaveTask(entry).execute();
+        // Save image for this page title
+        new PageImageSaveTask(app, app.getAPIForSite(title.getSite()), 
title).execute();
+
+        // animate the new fragment into place
+        // then hide the previous fragment.
+        final PageViewFragment prevFragment = curPageFragment;
+        fragmentPager.setOnAnimationListener(new 
PageFragmentPager.OnAnimationListener() {
+            @Override
+            public void OnAnimationFinished() {
+                if (prevFragment != null) {
+                    prevFragment.hide();
+                }
+                fragmentPager.setOnAnimationListener(null);
+            }
+        });
+
+        backStack.getStack().add(new BackStackItem(title, entry, 0));
+
+        fragmentAdapter.notifyDataSetChanged();
+        fragmentPager.setCurrentItem(backStack.size() - 1);
+
+        curPageFragment = 
(PageViewFragment)fragmentAdapter.getItem(fragmentPager.getCurrentItem());
+
+        Log.d("PageActivity", "pageBackStack has " + backStack.size() + " 
items");
+    }
+
+    /**
+     * Saving a history item needs to be in its own task, since the operation 
may
+     * actually block for several seconds, and should not be on the main 
thread.
+     */
+    private class HistorySaveTask extends SaneAsyncTask<Void> {
+        private final HistoryEntry entry;
+        public HistorySaveTask(HistoryEntry entry) {
+            super(SINGLE_THREAD);
+            this.entry = entry;
         }
-        curPageFragment = pageFragment;
+
+        @Override
+        public Void performTask() throws Throwable {
+            app.getPersister(HistoryEntry.class).persist(entry);
+            return null;
+        }
+
+        @Override
+        public void onCatch(Throwable caught) {
+            Log.d("HistorySaveTask", caught.getMessage());
+        }
     }
 
     @Subscribe
@@ -162,18 +248,35 @@
             drawerLayout.closeDrawer(Gravity.START);
             return;
         }
-        if (!searchAriclesFragment.handleBackPressed()
+        if (!searchArticlesFragment.handleBackPressed()
                 && !(curPageFragment != null && 
curPageFragment.handleBackPressed())) {
-            if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
+            if (backStack.size() <= 1) {
                 // Everything we could pop has been popped....
                 finish();
             } else {
-                getSupportFragmentManager().popBackStackImmediate();
-                String tag = 
getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount()
 - 1).getName();
-                curPageFragment = (PageViewFragment) 
getSupportFragmentManager().findFragmentByTag(tag);
+
+                // don't do anything if we're in the middle of an animation 
(looks better)
+                if (fragmentPager.isAnimating()) {
+                    return;
+                }
+
+                // let the Pager finish its animation, then remove the 
fragment that was moved off.
+                fragmentPager.setOnAnimationListener(new 
PageFragmentPager.OnAnimationListener() {
+                    @Override
+                    public void OnAnimationFinished() {
+                        fragmentAdapter.removeFragment(backStack.size() - 1);
+
+                        fragmentAdapter.notifyDataSetChanged();
+                        fragmentPager.setOnAnimationListener(null);
+                    }
+                });
+
+                fragmentPager.setCurrentItem(fragmentPager.getCurrentItem() - 
1);
+                curPageFragment = 
fragmentAdapter.getFragmentAt(fragmentPager.getCurrentItem());
                 curPageFragment.show();
-                searchAriclesFragment.clearErrors();
-                searchAriclesFragment.ensureVisible();
+
+                searchArticlesFragment.clearErrors();
+                searchArticlesFragment.ensureVisible();
             }
         }
     }
@@ -274,13 +377,19 @@
     @Override
     protected void onResume() {
         super.onResume();
-        boolean latestWikipediaZeroDispostion = 
WikipediaApp.getWikipediaZeroDisposition();
-        if (WikipediaApp.isWikipediaZeroDevmodeOn() && pausedStateOfZero && 
!latestWikipediaZeroDispostion) {
+        boolean latestWikipediaZeroDisposition = 
WikipediaApp.getWikipediaZeroDisposition();
+        if (WikipediaApp.isWikipediaZeroDevmodeOn() && pausedStateOfZero && 
!latestWikipediaZeroDisposition) {
             bus.post(new WikipediaZeroStateChangeEvent());
         }
+        fragmentAdapter.onResume(this);
         if (curPageFragment != null) {
             //refresh the current fragment's state (ensures correct state of 
overflow menu)
             curPageFragment.show();
+        }
+        // if we're just being resumed from a saved state, then sync 
curPageFragment
+        // with the correct item in the pager.
+        if (curPageFragment == null && fragmentAdapter.getCount() > 0) {
+            curPageFragment = 
(PageViewFragment)fragmentAdapter.getItem(fragmentPager.getCurrentItem());
         }
     }
 
@@ -296,9 +405,7 @@
         super.onSaveInstanceState(outState);
         outState.putBoolean("pausedStateOfZero", pausedStateOfZero);
         outState.putString("pausedXcsOfZero", pausedXcsOfZero);
-        if (curPageFragment != null) {
-            getSupportFragmentManager().putFragment(outState, 
"curPageFragment", curPageFragment);
-        }
+        outState.putParcelable("backStack", backStack);
     }
 
     @Override
diff --git 
a/wikipedia/src/main/java/org/wikipedia/page/PageFragmentAdapter.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageFragmentAdapter.java
new file mode 100644
index 0000000..f8e7acf
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageFragmentAdapter.java
@@ -0,0 +1,93 @@
+package org.wikipedia.page;
+
+import android.support.v4.app.*;
+import android.util.*;
+import android.view.*;
+import org.wikipedia.R;
+import java.util.List;
+
+public class PageFragmentAdapter extends FixedFragmentStatePagerAdapter {
+    private SparseArray<PageViewFragment> fragmentArray;
+    private BackStack backStack;
+
+    public PageFragmentAdapter(FragmentManager fm, BackStack backStack) {
+        super(fm);
+        this.backStack = backStack;
+        fragmentArray = new SparseArray<PageViewFragment>();
+    }
+
+    @Override
+    public Fragment getItem(int position) {
+        PageViewFragment f = fragmentArray.get(position);
+        if (f == null) {
+            f = new PageViewFragment(position,
+                    backStack.getStack().get(position).title,
+                    backStack.getStack().get(position).historyEntry,
+                    R.id.search_fragment);
+
+            fragmentArray.put(position, f);
+        }
+        return f;
+    }
+
+    public PageViewFragment getFragmentAt(int position) {
+        return fragmentArray.get(position);
+    }
+
+    @Override
+    public int getCount() {
+        return backStack.size();
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        super.notifyDataSetChanged();
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return fragmentArray.get(position).getTitle().getDisplayText();
+    }
+
+    /**
+     * Remove the specified fragment without saving its state.
+     * @param position Position at which to remove the fragment.
+     */
+    public void removeFragment(int position) {
+        PageViewFragment fragment = fragmentArray.get(position);
+        fragment.setSaveState(PageViewFragment.SAVE_STATE_NONE);
+        fragmentArray.delete(position);
+        backStack.getStack().remove(position);
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        PageViewFragment fragment = fragmentArray.get(position);
+        if (fragment != null) {
+            fragment.setSaveState(PageViewFragment.SAVE_STATE_TITLE);
+        }
+        super.destroyItem(container, position, object);
+    }
+
+    @Override
+    public int getItemPosition(Object item) {
+        if (fragmentArray.indexOfValue((PageViewFragment) item) == -1) {
+            return FragmentStatePagerAdapter.POSITION_NONE;
+        }
+        return FragmentStatePagerAdapter.POSITION_UNCHANGED;
+    }
+
+    /**
+     * Rebuilds this adapter's inner catalog of Fragments, useful for when
+     * the activity is restored from a saved state.
+     * @param activity Calling activity.
+     */
+    public void onResume(PageActivity activity) {
+        List<Fragment> fragments = 
activity.getSupportFragmentManager().getFragments();
+        for (Fragment f : fragments) {
+            if (f instanceof PageViewFragment) {
+                fragmentArray.put(((PageViewFragment) f).getPagerIndex(), 
(PageViewFragment)f);
+            }
+        }
+    }
+}
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageFragmentPager.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageFragmentPager.java
new file mode 100644
index 0000000..403f7e7
--- /dev/null
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageFragmentPager.java
@@ -0,0 +1,79 @@
+package org.wikipedia.page;
+
+import android.content.*;
+import android.support.v4.view.ViewPager;
+import android.util.*;
+import android.view.MotionEvent;
+
+public class PageFragmentPager extends ViewPager {
+
+    private boolean isPagingEnabled = true;
+    private OnAnimationListener onAnimationListener;
+    private boolean isAnimating = false;
+
+    public PageFragmentPager(Context context) {
+        super(context);
+        this.setOnPageChangeListener(new FragmentPagerListener());
+    }
+
+    public PageFragmentPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        this.setOnPageChangeListener(new FragmentPagerListener());
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (this.isPagingEnabled) {
+            return super.onTouchEvent(event);
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (this.isPagingEnabled) {
+            return super.onInterceptTouchEvent(event);
+        }
+        return false;
+    }
+
+    public void setPagingEnabled(boolean b) {
+        this.isPagingEnabled = b;
+    }
+
+    private class FragmentPagerListener implements 
ViewPager.OnPageChangeListener {
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int 
positionPixels) {
+        }
+        private boolean pageChanged = false;
+        @Override
+        public void onPageSelected(int position) {
+            pageChanged = true;
+        }
+        @Override
+        public void onPageScrollStateChanged(int state) {
+            if (state == ViewPager.SCROLL_STATE_IDLE) {
+                isAnimating = false;
+                if (pageChanged) {
+                    if (onAnimationListener != null) {
+                        onAnimationListener.OnAnimationFinished();
+                    }
+                }
+            } else {
+                isAnimating = true;
+            }
+        }
+    }
+
+    public boolean isAnimating() {
+        return isAnimating;
+    }
+
+    public interface OnAnimationListener {
+        public void OnAnimationFinished();
+    }
+
+    public void setOnAnimationListener(OnAnimationListener listener) {
+        onAnimationListener = listener;
+    }
+}
\ No newline at end of file
diff --git a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java 
b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
index 55196ee..65f2a80 100644
--- a/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
+++ b/wikipedia/src/main/java/org/wikipedia/page/PageViewFragment.java
@@ -27,12 +27,46 @@
     private static final String KEY_SCROLL_Y = "scrollY";
     private static final String KEY_CURRENT_HISTORY_ENTRY = 
"currentHistoryEntry";
     private static final String KEY_QUICK_RETURN_BAR_ID = "quickReturnBarId";
+    private static final String KEY_PAGER_INDEX = "pagerIndex";
 
     public static final int STATE_NO_FETCH = 1;
     public static final int STATE_INITIAL_FETCH = 2;
     public static final int STATE_COMPLETE_FETCH = 3;
 
     private int state = STATE_NO_FETCH;
+
+    /**
+     * Indicates that the full state of this fragment will be saved when 
onSaveInstanceState
+     * is called, including the heavy Page object that contains the full page 
text. This is good
+     * when the fragment is destroyed due to the activity being closed.
+     */
+    public static final int SAVE_STATE_FULL = 0;
+
+    /**
+     * Indicates that only a partial state of this fragment will be saved when 
onSaveInstanceState
+     * is called, including the Title, HistoryItem, and scroll position. This 
is used when the
+     * fragment is destroyed by the ViewPager when the user slides it out of 
sight. The next time
+     * the fragment is recreated, we'll fetch the page contents from the 
network again.
+     */
+    public static final int SAVE_STATE_TITLE = 1;
+
+    /**
+     * Indicates that none of this fragment's state will be saved when 
onSaveInstanceState
+     * is called. This is used when the fragment is destroyed due to the user 
going "back"
+     * in the ViewPager.
+     */
+    public static final int SAVE_STATE_NONE = 2;
+
+    /**
+     * Determines how much of this fragment's state will be saved when it's 
destroyed.
+     * (when onSaveInstanceState is called)
+     */
+    private int saveState = SAVE_STATE_FULL;
+
+    /**
+     * Stores this fragment's position in the ViewPager in the parent activity.
+     */
+    private int pagerIndex;
 
     private PageTitle title;
     private ObservableWebView webView;
@@ -41,7 +75,7 @@
     private View retryButton;
     private View pageDoesNotExistError;
     private DisableableDrawerLayout tocDrawer;
-    private FrameLayout pageFragmentContainer;
+    private View pageFragmentContainer;
 
     private Page page;
     private HistoryEntry curEntry;
@@ -61,7 +95,8 @@
     private ReadingActionFunnel readingActionFunnel;
 
     // Pass in the id rather than the View object itself for the quickReturn 
bar, to help it survive rotates
-    public PageViewFragment(PageTitle title, HistoryEntry historyEntry, int 
quickReturnBarId) {
+    public PageViewFragment(int pagerIndex, PageTitle title, HistoryEntry 
historyEntry, int quickReturnBarId) {
+        this.pagerIndex = pagerIndex;
         this.title = title;
         this.curEntry = historyEntry;
         this.quickReturnBarId = quickReturnBarId;
@@ -78,12 +113,41 @@
         return page;
     }
 
+    public HistoryEntry getHistoryEntry() {
+        return curEntry;
+    }
+
+    public int getScrollY() {
+        if (webView != null) {
+            scrollY = webView.getScrollY();
+        }
+        return scrollY;
+    }
+
+    public void setScrollY(int scrollY) {
+        this.scrollY = scrollY;
+        if (webView != null) {
+            webView.scrollTo(0, scrollY);
+        }
+    }
+
+    public void setSaveState(int saveState) {
+        this.saveState = saveState;
+    }
+
+    public int getPagerIndex() {
+        return pagerIndex;
+    }
+
     /*
     Hide the entire fragment. This is necessary when displaying a new page 
fragment on top
     of a previous one -- some devices have issues with rendering "heavy" 
components
     (like WebView) when overlaid on top of many other Views.
      */
     public void hide() {
+        if (pageFragmentContainer == null) {
+            return;
+        }
         pageFragmentContainer.setVisibility(View.GONE);
     }
 
@@ -92,6 +156,9 @@
     stack of fragments
      */
     public void show() {
+        if (pageFragmentContainer == null) {
+            return;
+        }
         pageFragmentContainer.setVisibility(View.VISIBLE);
         //refresh the fragment's state (ensures correct state of overflow menu)
         setState(state);
@@ -126,12 +193,17 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putParcelable(KEY_TITLE, title);
-        outState.putParcelable(KEY_PAGE, page);
-        outState.putInt(KEY_STATE, state);
-        outState.putInt(KEY_SCROLL_Y, webView.getScrollY());
-        outState.putParcelable(KEY_CURRENT_HISTORY_ENTRY, curEntry);
-        outState.putInt(KEY_QUICK_RETURN_BAR_ID, quickReturnBarId);
+        if (saveState != SAVE_STATE_NONE) {
+            outState.putInt(KEY_PAGER_INDEX, pagerIndex);
+            outState.putParcelable(KEY_TITLE, title);
+            outState.putInt(KEY_SCROLL_Y, webView.getScrollY());
+            outState.putParcelable(KEY_CURRENT_HISTORY_ENTRY, curEntry);
+            outState.putInt(KEY_QUICK_RETURN_BAR_ID, quickReturnBarId);
+            if (saveState == SAVE_STATE_FULL) {
+                outState.putParcelable(KEY_PAGE, page);
+                outState.putInt(KEY_STATE, state);
+            }
+        }
     }
 
     @Override
@@ -144,13 +216,14 @@
         super.onActivityCreated(savedInstanceState);
         if (savedInstanceState != null && 
savedInstanceState.containsKey(KEY_TITLE)) {
             title = savedInstanceState.getParcelable(KEY_TITLE);
+            curEntry = 
savedInstanceState.getParcelable(KEY_CURRENT_HISTORY_ENTRY);
+            scrollY = savedInstanceState.getInt(KEY_SCROLL_Y);
+            quickReturnBarId = 
savedInstanceState.getInt(KEY_QUICK_RETURN_BAR_ID);
+            pagerIndex = savedInstanceState.getInt(KEY_PAGER_INDEX);
             if (savedInstanceState.containsKey(KEY_PAGE)) {
                 page = savedInstanceState.getParcelable(KEY_PAGE);
+                state = savedInstanceState.getInt(KEY_STATE);
             }
-            state = savedInstanceState.getInt(KEY_STATE);
-            scrollY = savedInstanceState.getInt(KEY_SCROLL_Y);
-            curEntry = 
savedInstanceState.getParcelable(KEY_CURRENT_HISTORY_ENTRY);
-            quickReturnBarId = 
savedInstanceState.getInt(KEY_QUICK_RETURN_BAR_ID);
         }
         if (title == null) {
             throw new RuntimeException("No PageTitle passed in to constructor 
or in instanceState");
@@ -158,7 +231,7 @@
 
         app = (WikipediaApp)getActivity().getApplicationContext();
 
-        pageFragmentContainer = (FrameLayout) 
getView().findViewById(R.id.page_fragment_container);
+        pageFragmentContainer = 
getView().findViewById(R.id.page_fragment_container);
         webView = (ObservableWebView) 
getView().findViewById(R.id.page_web_view);
         loadProgress = (ProgressBar) 
getView().findViewById(R.id.page_load_progress);
         networkError = getView().findViewById(R.id.page_error);
@@ -319,10 +392,6 @@
             displayLeadSection();
             setState(STATE_INITIAL_FETCH);
             new RestSectionsFetchTask().execute();
-
-            // Add history entry now
-            app.getPersister(HistoryEntry.class).persist(curEntry);
-            new PageImageSaveTask(app, api, title).execute();
         }
 
         @Override
@@ -390,4 +459,10 @@
         }
         return false;
     }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d("PageViewFragment", "Fragment destroyed.");
+    }
 }

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I768e2dcae18924d78253fad66bfaa83b8447d94a
Gerrit-PatchSet: 15
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Dbrant <[email protected]>
Gerrit-Reviewer: BearND <[email protected]>
Gerrit-Reviewer: Brion VIBBER <[email protected]>
Gerrit-Reviewer: Yuvipanda <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to