Restore more missing DirectoryFragment functionality.

- Add a material-style horizontal progress bar to DocumentsUI.  This
  replaces the old loading footer.
- Restore the view-switching code that displayed a dedicated "empty"
  view when displaying an empty directory.
- Fix tests.

Change-Id: Ifecb867f6edecbdeb37b8607d87d9797879a589d
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index c2821e1..b30cab6 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -135,6 +135,7 @@
     private static final String EXTRA_IGNORE_STATE = "ignoreState";
 
     private Model mModel;
+    private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -160,6 +161,7 @@
     private int mColumnCount = 1;  // This will get updated when layout changes.
 
     private MessageBar mMessageBar;
+    private View mProgressBar;
 
     public static void showNormal(FragmentManager fm, RootInfo root, DocumentInfo doc, int anim) {
         show(fm, TYPE_NORMAL, root, doc, null, anim);
@@ -223,6 +225,7 @@
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
         mMessageBar = MessageBar.create(getChildFragmentManager());
+        mProgressBar = view.findViewById(R.id.progressbar);
 
         mEmptyView = view.findViewById(android.R.id.empty);
 
@@ -311,9 +314,8 @@
                     : MultiSelectManager.MODE_SINGLE);
         selMgr.addCallback(new SelectionModeListener());
 
-        mModel = new Model(context, selMgr);
-        mModel.setSelectionManager(selMgr);
-        mModel.addUpdateListener(mAdapter);
+        mModel = new Model(context, selMgr, mAdapter);
+        mModel.addUpdateListener(mModelUpdateListener);
 
         mType = getArguments().getInt(EXTRA_TYPE);
         mStateKey = buildStateKey(root, doc);
@@ -897,8 +899,7 @@
         }
     }
 
-    private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder>
-            implements Model.UpdateListener {
+    private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
 
         private final Context mContext;
         private final LayoutInflater mInflater;
@@ -909,30 +910,6 @@
         }
 
         @Override
-        public void onModelUpdate(Model model) {
-            if (model.info != null || model.error != null) {
-                mMessageBar.setInfo(model.info);
-                mMessageBar.setError(model.error);
-                mMessageBar.show();
-            }
-
-            if (model.isEmpty()) {
-                mEmptyView.setVisibility(View.VISIBLE);
-            } else {
-                mEmptyView.setVisibility(View.GONE);
-            }
-
-            notifyDataSetChanged();
-        }
-
-        @Override
-        public void onModelUpdateFailed(Exception e) {
-            // TODO: deal with catastrophic update failures
-            String error = getString(R.string.query_error);
-            notifyDataSetChanged();
-        }
-
-        @Override
         public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             final State state = getDisplayState(DirectoryFragment.this);
             final LayoutInflater inflater = LayoutInflater.from(getContext());
@@ -1675,6 +1652,7 @@
     @VisibleForTesting
     public static final class Model implements DocumentContext {
         private MultiSelectManager mSelectionManager;
+        private RecyclerView.Adapter<?> mViewAdapter;
         private Context mContext;
         private int mCursorCount;
         private boolean mIsLoading;
@@ -1684,17 +1662,11 @@
         @Nullable private String info;
         @Nullable private String error;
 
-        Model(Context context, MultiSelectManager selectionManager) {
+        Model(Context context, MultiSelectManager selectionManager,
+                RecyclerView.Adapter<?> viewAdapter) {
             mContext = context;
             mSelectionManager = selectionManager;
-        }
-
-        /**
-         * Sets the selection manager used by the model.
-         * TODO: the model should instantiate the selection manager.  See onActivityCreated.
-         */
-        void setSelectionManager(MultiSelectManager mgr) {
-            mSelectionManager = mgr;
+            mViewAdapter = viewAdapter;
         }
 
         /**
@@ -1859,7 +1831,7 @@
                 int position = selected.get(i);
                 if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
                 mMarkedForDeletion.append(position, true);
-                mUpdateListener.notifyItemRemoved(position);
+                mViewAdapter.notifyItemRemoved(position);
             }
         }
 
@@ -1874,7 +1846,7 @@
             for (int i = 0; i < size; ++i) {
                 final int position = mMarkedForDeletion.keyAt(i);
                 mMarkedForDeletion.put(position, false);
-                mUpdateListener.notifyItemInserted(position);
+                mViewAdapter.notifyItemInserted(position);
             }
 
             // Then, clear the deletion list.
@@ -1957,26 +1929,46 @@
             mUpdateListener = listener;
         }
 
-        interface UpdateListener {
+        static class UpdateListener {
             /**
              * Called when a successful update has occurred.
              */
-            void onModelUpdate(Model model);
+            void onModelUpdate(Model model) {}
 
             /**
              * Called when an update has been attempted but failed.
              */
-            void onModelUpdateFailed(Exception e);
+            void onModelUpdateFailed(Exception e) {}
+        }
+    }
 
-            /**
-             * Called when an item has been removed from the model.
-             */
-            void notifyItemRemoved(int position);
+    private class ModelUpdateListener extends Model.UpdateListener {
+        @Override
+        public void onModelUpdate(Model model) {
+            if (model.info != null || model.error != null) {
+                mMessageBar.setInfo(model.info);
+                mMessageBar.setError(model.error);
+                mMessageBar.show();
+            }
 
-            /**
-             * Called when an item has been added to the model.
-             */
-            void notifyItemInserted(int position);
+            mProgressBar.setVisibility(model.isLoading() ? View.VISIBLE : View.GONE);
+
+            if (model.isEmpty()) {
+                mEmptyView.setVisibility(View.VISIBLE);
+                mRecView.setVisibility(View.GONE);
+            } else {
+                mEmptyView.setVisibility(View.GONE);
+                mRecView.setVisibility(View.VISIBLE);
+            }
+
+            mAdapter.notifyDataSetChanged();
+        }
+
+        @Override
+        public void onModelUpdateFailed(Exception e) {
+            // TODO: deal with catastrophic update failures
+            String error = getString(R.string.query_error);
+            mAdapter.notifyDataSetChanged();
         }
     }
 }