Bug 1062859 - Part 1 - Restore scroll position when going back through bookmark folders. r?ahunt draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Sun, 22 Jan 2017 22:02:35 +0100
changeset 468113 d8d1d22ba11fafe559ac0d2f911e4a0c0c78dd32
parent 468112 64488a3957cced57a6fe3f1e1b88c837bae243b2
child 543849 9e2f9247d929d95171c99e40676d3bd6e2e24f24
push id43355
push usermozilla@buttercookie.de
push dateMon, 30 Jan 2017 19:01:15 +0000
reviewersahunt
bugs1062859
milestone54.0a1
Bug 1062859 - Part 1 - Restore scroll position when going back through bookmark folders. r?ahunt When going down one folder level, we store the current scroll position of the list view and then scroll to the top of the list, so that we always show a child folder starting from the beginning. When navigating back up to its parent, we then restore the previously stored scroll position. MozReview-Commit-ID: 9C2RMXrlUm1
mobile/android/base/java/org/mozilla/gecko/home/BookmarksListAdapter.java
mobile/android/base/java/org/mozilla/gecko/home/BookmarksListView.java
mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
--- a/mobile/android/base/java/org/mozilla/gecko/home/BookmarksListAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BookmarksListAdapter.java
@@ -57,39 +57,42 @@ class BookmarksListAdapter extends Multi
                 return new RefreshType[size];
             }
         };
     }
 
     public static class FolderInfo implements Parcelable {
         public final int id;
         public final String title;
+        public final int position;
 
         public FolderInfo(int id) {
-            this(id, "");
+            this(id, "", 0);
         }
 
         public FolderInfo(Parcel in) {
-            this(in.readInt(), in.readString());
+            this(in.readInt(), in.readString(), in.readInt());
         }
 
-        public FolderInfo(int id, String title) {
+        public FolderInfo(int id, String title, int position) {
             this.id = id;
             this.title = title;
+            this.position = position;
         }
 
         @Override
         public int describeContents() {
             return 0;
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(id);
             dest.writeString(title);
+            dest.writeInt(position);
         }
 
         public static final Creator<FolderInfo> CREATOR = new Creator<FolderInfo>() {
             @Override
             public FolderInfo createFromParcel(Parcel in) {
                 return new FolderInfo(in);
             }
 
@@ -99,17 +102,19 @@ class BookmarksListAdapter extends Multi
             }
         };
     }
 
     // A listener that knows how to refresh the list for a given folder id.
     // This is usually implemented by the enclosing fragment/activity.
     public static interface OnRefreshFolderListener {
         // The folder id to refresh the list with.
-        public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType);
+        public void onRefreshFolder(FolderInfo folderInfo,
+                                    RefreshType refreshType,
+                                    int targetPosition);
     }
 
     /**
      * The type of data a bookmarks folder can display. This can be used to
      * distinguish bookmark folders from "smart folders" that contain non-bookmark
      * entries but still appear in the Bookmarks panel.
      */
     public enum FolderType {
@@ -159,35 +164,38 @@ class BookmarksListAdapter extends Multi
         // If we're already at the root, we can't move to a parent folder.
         // An empty parent stack here means we're still waiting for the
         // initial list of bookmarks and can't go to a parent folder.
         if (mParentStack.size() <= 1) {
             return false;
         }
 
         if (mListener != null) {
+            int targetPosition = mParentStack.peek().position;
             // We pick the second folder in the stack as it represents
             // the parent folder.
-            mListener.onRefreshFolder(mParentStack.get(1), RefreshType.PARENT);
+            mListener.onRefreshFolder(mParentStack.get(1), RefreshType.PARENT, targetPosition);
         }
 
         return true;
     }
 
     /**
      * Moves to child folder, given a folderId.
      *
      * @param folderId The id of the folder to show.
      * @param folderTitle The title of the folder to show.
+     * @param position The current position of the list view, which
+     *                 will be restored when moving back up one level.
      */
-    public void moveToChildFolder(int folderId, String folderTitle) {
-        FolderInfo folderInfo = new FolderInfo(folderId, folderTitle);
+    public void moveToChildFolder(int folderId, String folderTitle, int position) {
+        FolderInfo folderInfo = new FolderInfo(folderId, folderTitle, position);
 
         if (mListener != null) {
-            mListener.onRefreshFolder(folderInfo, RefreshType.CHILD);
+            mListener.onRefreshFolder(folderInfo, RefreshType.CHILD, 0 /* scroll to top of list */);
         }
     }
 
     /**
      * Set a listener that can refresh this adapter.
      *
      * @param listener The listener that can refresh the adapter.
      */
--- a/mobile/android/base/java/org/mozilla/gecko/home/BookmarksListView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BookmarksListView.java
@@ -149,17 +149,17 @@ public class BookmarksListView extends H
             return;
         }
 
         int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
         if (type == Bookmarks.TYPE_FOLDER) {
             // If we're clicking on a folder, update adapter to move to that folder
             final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
             final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor);
-            adapter.moveToChildFolder(folderId, folderTitle);
+            adapter.moveToChildFolder(folderId, folderTitle, getFirstVisiblePosition());
 
             final List<BookmarksListAdapter.FolderInfo> parentStack = ((BookmarksListAdapter) getAdapter()).getParentStack();
 
             final int baseFolderID;
             if (parentStack.size() > 2) {
                 baseFolderID = parentStack.get(parentStack.size() - 2).id;
             } else {
                 baseFolderID = Bookmarks.FIXED_ROOT_ID;
--- a/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
@@ -49,16 +49,19 @@ public class BookmarksPanel extends Home
     private static final int LOADER_ID_BOOKMARKS_LIST = 0;
 
     // Information about the target bookmarks folder.
     private static final String BOOKMARKS_FOLDER_INFO = "folder_info";
 
     // Refresh type for folder refreshing loader.
     private static final String BOOKMARKS_REFRESH_TYPE = "refresh_type";
 
+    // Position that the list view should be scrolled to after loading has finished.
+    private static final String BOOKMARKS_SCROLL_POSITION = "listview_position";
+
     // List of bookmarks.
     private BookmarksListView mList;
 
     // Adapter for list of bookmarks.
     private BookmarksListAdapter mListAdapter;
 
     // Adapter's parent stack.
     private List<FolderInfo> mSavedParentStack;
@@ -132,21 +135,24 @@ public class BookmarksPanel extends Home
         super.onActivityCreated(savedInstanceState);
 
         final Activity activity = getActivity();
 
         // Setup the list adapter.
         mListAdapter = new BookmarksListAdapter(activity, null, mSavedParentStack);
         mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() {
             @Override
-            public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType) {
+            public void onRefreshFolder(FolderInfo folderInfo,
+                                        RefreshType refreshType ,
+                                        int targetPosition) {
                 // Restart the loader with folder as the argument.
                 Bundle bundle = new Bundle();
                 bundle.putParcelable(BOOKMARKS_FOLDER_INFO, folderInfo);
                 bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, refreshType);
+                bundle.putInt(BOOKMARKS_SCROLL_POSITION, targetPosition);
                 getLoaderManager().restartLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks);
             }
         });
         mList.setAdapter(mListAdapter);
 
         // Create callbacks before the initial loader is started.
         mLoaderCallbacks = new CursorLoaderCallbacks();
         loadIfVisible();
@@ -202,28 +208,33 @@ public class BookmarksPanel extends Home
     }
 
     /**
      * Loader for the list for bookmarks.
      */
     private static class BookmarksLoader extends SimpleCursorLoader {
         private final FolderInfo mFolderInfo;
         private final RefreshType mRefreshType;
+        private final int mTargetPosition;
         private final BrowserDB mDB;
 
         public BookmarksLoader(Context context) {
             this(context,
-                 new FolderInfo(Bookmarks.FIXED_ROOT_ID, context.getResources().getString(R.string.bookmarks_title)),
-                 RefreshType.CHILD);
+                 new FolderInfo(Bookmarks.FIXED_ROOT_ID, context.getResources().getString(R.string.bookmarks_title), 0),
+                 RefreshType.CHILD, 0);
         }
 
-        public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) {
+        public BookmarksLoader(Context context,
+                               FolderInfo folderInfo,
+                               RefreshType refreshType,
+                               int targetPosition) {
             super(context);
             mFolderInfo = folderInfo;
             mRefreshType = refreshType;
+            mTargetPosition = targetPosition;
             mDB = BrowserDB.from(context);
         }
 
         @Override
         public Cursor loadCursor() {
             final boolean isRootFolder = mFolderInfo.id == BrowserContract.Bookmarks.FIXED_ROOT_ID;
 
             final ContentResolver contentResolver = getContext().getContentResolver();
@@ -262,30 +273,35 @@ public class BookmarksPanel extends Home
 
         public FolderInfo getFolderInfo() {
             return mFolderInfo;
         }
 
         public RefreshType getRefreshType() {
             return mRefreshType;
         }
+
+        public int getTargetPosition() {
+            return mTargetPosition;
+        }
     }
 
     /**
      * Loader callbacks for the LoaderManager of this fragment.
      */
     private class CursorLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
         @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
             if (args == null) {
                 return new BookmarksLoader(getActivity());
             } else {
                 FolderInfo folderInfo = (FolderInfo) args.getParcelable(BOOKMARKS_FOLDER_INFO);
                 RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE);
-                return new BookmarksLoader(getActivity(), folderInfo, refreshType);
+                final int targetPosition = args.getInt(BOOKMARKS_SCROLL_POSITION);
+                return new BookmarksLoader(getActivity(), folderInfo, refreshType, targetPosition);
             }
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
             BookmarksLoader bl = (BookmarksLoader) loader;
             mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType());
 
@@ -296,16 +312,20 @@ public class BookmarksPanel extends Home
                 // Bundle likes to store ArrayLists or Arrays, but we've got a generic List (which
                 // is actually an unmodifiable wrapper around a LinkedList). We'll need to do a
                 // LinkedList conversion at the other end, when saving we need to use this awkward
                 // syntax to create an Array.
                 bundle.putParcelableArrayList("parentStack", new ArrayList<FolderInfo>(parentStack));
 
                 mPanelStateChangeListener.onStateChanged(bundle);
             }
+
+            if (mList != null) {
+                mList.setSelection(bl.getTargetPosition());
+            }
             updateUiFromCursor(c);
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             if (mList != null) {
                 mListAdapter.swapCursor(null);
             }