Bug 929982 - Update parent stack and cursor at the same time (r=margaret, a=bajaj)
authorLucas Rocha <lucasr@mozilla.com>
Wed, 20 Nov 2013 19:51:05 +0000
changeset 167522 a4bb868db6fb3187833f9ffc6b84cf310b6f7f65
parent 167521 edcf89fbe953b64a1c6c47134cf13ffac9641d70
child 167523 6ce5d00f1aa8d16479020a44e75b61ab343cec8b
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret, bajaj
bugs929982
milestone27.0a2
Bug 929982 - Update parent stack and cursor at the same time (r=margaret, a=bajaj)
mobile/android/base/home/BookmarksListAdapter.java
mobile/android/base/home/BookmarksPage.java
--- a/mobile/android/base/home/BookmarksListAdapter.java
+++ b/mobile/android/base/home/BookmarksListAdapter.java
@@ -6,123 +6,189 @@
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.view.View;
 
 import java.util.Collections;
 import java.util.List;
 import java.util.LinkedList;
 
 /**
  * Adapter to back the BookmarksListView with a list of bookmarks.
  */
 class BookmarksListAdapter extends MultiTypeCursorAdapter {
     private static final int VIEW_TYPE_ITEM = 0;
     private static final int VIEW_TYPE_FOLDER = 1;
 
     private static final int[] VIEW_TYPES = new int[] { VIEW_TYPE_ITEM, VIEW_TYPE_FOLDER };
     private static final int[] LAYOUT_TYPES = new int[] { R.layout.bookmark_item_row, R.layout.bookmark_folder_row };
 
-    public class FolderInfo {
+    public enum RefreshType implements Parcelable {
+        PARENT,
+        CHILD;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(ordinal());
+        }
+
+        public static final Creator<RefreshType> CREATOR = new Creator<RefreshType>() {
+            @Override
+            public RefreshType createFromParcel(final Parcel source) {
+                return RefreshType.values()[source.readInt()];
+            }
+
+            @Override
+            public RefreshType[] newArray(final int size) {
+                return new RefreshType[size];
+            }
+        };
+    }
+
+    public static class FolderInfo implements Parcelable {
         public final int id;
         public final String title;
 
         public FolderInfo(int id) {
             this(id, "");
         }
 
+        public FolderInfo(Parcel in) {
+            this(in.readInt(), in.readString());
+        }
+
         public FolderInfo(int id, String title) {
             this.id = id;
             this.title = title;
         }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(id);
+            dest.writeString(title);
+        }
+
+        public static final Creator<FolderInfo> CREATOR = new Creator<FolderInfo>() {
+            public FolderInfo createFromParcel(Parcel in) {
+                return new FolderInfo(in);
+            }
+
+            public FolderInfo[] newArray(int size) {
+                return new FolderInfo[size];
+            }
+        };
     }
 
     // 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(int folderId);
+        public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType);
     }
 
     // mParentStack holds folder info instances (id + title) that allow
     // us to navigate back up the folder hierarchy.
     private LinkedList<FolderInfo> mParentStack;
 
     // Refresh folder listener.
     private OnRefreshFolderListener mListener;
 
     public BookmarksListAdapter(Context context, Cursor cursor, List<FolderInfo> parentStack) {
         // Initializing with a null cursor.
         super(context, cursor, VIEW_TYPES, LAYOUT_TYPES);
 
         if (parentStack == null) {
             mParentStack = new LinkedList<FolderInfo>();
-
-            // Add the root folder to the stack
-            FolderInfo rootFolder = new FolderInfo(Bookmarks.FIXED_ROOT_ID);
-            mParentStack.addFirst(rootFolder);
         } else {
             mParentStack = new LinkedList<FolderInfo>(parentStack);
         }
     }
 
     public List<FolderInfo> getParentStack() {
         return Collections.unmodifiableList(mParentStack);
     }
 
-    // Refresh the current folder by executing a new task.
-    private void refreshCurrentFolder() {
-        if (mListener != null) {
-            mListener.onRefreshFolder(mParentStack.peek().id);
-        }
-    }
-
     /**
      * Moves to parent folder, if one exists.
      *
      * @return Whether the adapter successfully moved to a parent folder.
      */
     public boolean moveToParentFolder() {
         // If we're already at the root, we can't move to a parent folder
         if (mParentStack.size() == 1) {
             return false;
         }
 
-        mParentStack.removeFirst();
-        refreshCurrentFolder();
+        if (mListener != null) {
+            // We pick the second folder in the stack as it represents
+            // the parent folder.
+            mListener.onRefreshFolder(mParentStack.get(1), RefreshType.PARENT);
+        }
+
         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.
      */
     public void moveToChildFolder(int folderId, String folderTitle) {
         FolderInfo folderInfo = new FolderInfo(folderId, folderTitle);
-        mParentStack.addFirst(folderInfo);
-        refreshCurrentFolder();
+
+        if (mListener != null) {
+            mListener.onRefreshFolder(folderInfo, RefreshType.CHILD);
+        }
     }
 
     /**
      * Set a listener that can refresh this adapter.
      *
      * @param listener The listener that can refresh the adapter.
      */
     public void setOnRefreshFolderListener(OnRefreshFolderListener listener) {
         mListener = listener;
     }
 
+    public void swapCursor(Cursor c, FolderInfo folderInfo, RefreshType refreshType) {
+        switch(refreshType) {
+            case PARENT:
+                mParentStack.removeFirst();
+                break;
+
+            case CHILD:
+                mParentStack.addFirst(folderInfo);
+                break;
+
+            default:
+                // Do nothing;
+        }
+
+        swapCursor(c);
+    }
+
     @Override
     public int getItemViewType(int position) {
         // The position also reflects the opened child folder row.
         if (isShowingChildFolder()) {
             if (position == 0) {
                 return VIEW_TYPE_FOLDER;
             }
 
@@ -171,16 +237,20 @@ class BookmarksListAdapter extends Multi
         // the special folders we expect in the UI, just return the title from the DB.
         return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
     }
 
     /**
      * @return true, if currently showing a child folder, false otherwise.
      */
     public boolean isShowingChildFolder() {
+        if (mParentStack.size() == 0) {
+            return false;
+        }
+
         return (mParentStack.peek().id != Bookmarks.FIXED_ROOT_ID);
     }
 
     @Override
     public int getCount() {
         return super.getCount() + (isShowingChildFolder() ? 1 : 0);
     }
 
--- a/mobile/android/base/home/BookmarksPage.java
+++ b/mobile/android/base/home/BookmarksPage.java
@@ -5,16 +5,17 @@
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo;
 import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
+import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
@@ -32,18 +33,21 @@ import java.util.List;
  * A page in about:home that displays a ListView of bookmarks.
  */
 public class BookmarksPage extends HomeFragment {
     public static final String LOGTAG = "GeckoBookmarksPage";
 
     // Cursor loader ID for list of bookmarks.
     private static final int LOADER_ID_BOOKMARKS_LIST = 0;
 
-    // Key for bookmarks folder id.
-    private static final String BOOKMARKS_FOLDER_KEY = "folder_id";
+    // 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";
 
     // List of bookmarks.
     private BookmarksListView mList;
 
     // Adapter for list of bookmarks.
     private BookmarksListAdapter mListAdapter;
 
     // Adapter's parent stack.
@@ -87,20 +91,21 @@ public class BookmarksPage extends HomeF
         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(int folderId) {
+            public void onRefreshFolder(FolderInfo folderInfo, RefreshType refreshType) {
                 // Restart the loader with folder as the argument.
                 Bundle bundle = new Bundle();
-                bundle.putInt(BOOKMARKS_FOLDER_KEY, folderId);
+                bundle.putParcelable(BOOKMARKS_FOLDER_INFO, folderInfo);
+                bundle.putParcelable(BOOKMARKS_REFRESH_TYPE, refreshType);
                 getLoaderManager().restartLoader(LOADER_ID_BOOKMARKS_LIST, bundle, mLoaderCallbacks);
             }
         });
         mList.setAdapter(mListAdapter);
 
         // Invalidate the cached value that keeps track of whether or
         // not desktop bookmarks (or reading list items) exist.
         BrowserDB.invalidateCachedState();
@@ -162,49 +167,62 @@ public class BookmarksPage extends HomeF
             mList.setEmptyView(mEmptyView);
         }
     }
 
     /**
      * Loader for the list for bookmarks.
      */
     private static class BookmarksLoader extends SimpleCursorLoader {
-        private final int mFolderId;
+        private final FolderInfo mFolderInfo;
+        private final RefreshType mRefreshType;
 
         public BookmarksLoader(Context context) {
-            this(context, Bookmarks.FIXED_ROOT_ID);
+            this(context, new FolderInfo(Bookmarks.FIXED_ROOT_ID), RefreshType.CHILD);
         }
 
-        public BookmarksLoader(Context context, int folderId) {
+        public BookmarksLoader(Context context, FolderInfo folderInfo, RefreshType refreshType) {
             super(context);
-            mFolderId = folderId;
+            mFolderInfo = folderInfo;
+            mRefreshType = refreshType;
         }
 
         @Override
         public Cursor loadCursor() {
-            return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderId);
+            return BrowserDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id);
+        }
+
+        public FolderInfo getFolderInfo() {
+            return mFolderInfo;
+        }
+
+        public RefreshType getRefreshType() {
+            return mRefreshType;
         }
     }
 
     /**
      * Loader callbacks for the LoaderManager of this fragment.
      */
     private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
         @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
             if (args == null) {
                 return new BookmarksLoader(getActivity());
             } else {
-                return new BookmarksLoader(getActivity(), args.getInt(BOOKMARKS_FOLDER_KEY));
+                FolderInfo folderInfo = (FolderInfo) args.getParcelable(BOOKMARKS_FOLDER_INFO);
+                RefreshType refreshType = (RefreshType) args.getParcelable(BOOKMARKS_REFRESH_TYPE);
+                return new BookmarksLoader(getActivity(), folderInfo, refreshType);
             }
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
-            mListAdapter.swapCursor(c);
+            BookmarksLoader bl = (BookmarksLoader) loader;
+            mListAdapter.swapCursor(c, bl.getFolderInfo(), bl.getRefreshType());
             updateUiFromCursor(c);
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             if (mList != null) {
                 mListAdapter.swapCursor(null);
             }