Drawer updates for Media app

- Merging list and empty adapter functionality into CarDrawerAdapter.
  The adapter will display disabled-list item if flag set and no items
  are available to display.
- CarDrawerAdapter now supports cleanup when popped. Subclasses can perform
  any required cleanup.
- CarDrawerAdapter sets title from resource-string or bare-string.
  Media-app needs latter while Dialer/Radio use former.
- CarDrawerActivity supports setting main content view. MediaApp will
  use this (in upcoming CL).

Bug: 34352155
Test: Manually
Change-Id: Idf99cfaf8ae8269756a722e883ecf64df9dcbdbe
diff --git a/car-stream-ui-lib/src/com/android/car/app/CarDrawerActivity.java b/car-stream-ui-lib/src/com/android/car/app/CarDrawerActivity.java
index 39e9838..e1198d4 100644
--- a/car-stream-ui-lib/src/com/android/car/app/CarDrawerActivity.java
+++ b/car-stream-ui-lib/src/com/android/car/app/CarDrawerActivity.java
@@ -18,14 +18,17 @@
 
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.support.annotation.LayoutRes;
 import android.support.car.ui.PagedListView;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.app.ActionBarDrawerToggle;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ProgressBar;
 
 import com.android.car.stream.ui.R;
@@ -38,8 +41,9 @@
  * This Activity manages the overall layout. To use it sub-classes need to:
  * <ul>
  *     <li>Provide the root-items for the Drawer by implementing {@link #getRootAdapter()}.</li>
- *     <li>Add their main content to the container FrameLayout
- *     (with id = {@link #getContentContainerId()}_</li>
+ *     <li>Add their main content using {@link #setMainContent(int)} or
+ *     {@link #setMainContent(View)}. They can also add fragments to the main-content container by
+ *     obtaining its id using {@link #getContentContainerId()}</li>
  * </ul>
  * This class will take care of drawer toggling and display.
  * <p>
@@ -81,12 +85,46 @@
         // Init drawer adapter stack.
         CarDrawerAdapter rootAdapter = getRootAdapter();
         mAdapterStack.push(rootAdapter);
-        mToolbar.setTitle(rootAdapter.getTitleResId());
+        setToolbarTitleFrom(rootAdapter);
         mDrawerList.setAdapter(rootAdapter);
 
         setupDrawerToggling();
     }
 
+    private void setToolbarTitleFrom(CarDrawerAdapter adapter) {
+        if (adapter.getTitleResId() != CarDrawerAdapter.INVALID_STRING_RES_ID) {
+            mToolbar.setTitle(adapter.getTitleResId());
+        } else if (adapter.getTitleString() != null) {
+            mToolbar.setTitle(adapter.getTitleString());
+        } else {
+            throw new RuntimeException("CarDrawerAdapter must supply title via " +
+                " getTitleResId() or getTitleString()");
+        }
+    }
+
+    /**
+     * Set main content to display in this Activity. It will be added to R.id.content_frame in
+     * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(View)}.
+     *
+     * @param view View to display as main content.
+     */
+    public void setMainContent(View view) {
+        ViewGroup parent = (ViewGroup) findViewById(getContentContainerId());
+        parent.addView(view);
+    }
+
+    /**
+     * Set main content to display in this Activity. It will be added to R.id.content_frame in
+     * car_drawer_activity.xml. NOTE: Do not use {@link #setContentView(int)}.
+     *
+     * @param resourceId Layout to display as main content.
+     */
+    public void setMainContent(@LayoutRes int resourceId) {
+        ViewGroup parent = (ViewGroup) findViewById(getContentContainerId());
+        LayoutInflater inflater = getLayoutInflater();
+        inflater.inflate(resourceId, parent, true);
+    }
+
     /**
      * @return Adapter for root content of the Drawer.
      */
@@ -100,7 +138,7 @@
      *
      * @param adapter Adapter for next level of content in the drawer.
      */
-    protected final void switchToAdapter(CarDrawerAdapter adapter) {
+    public final void switchToAdapter(CarDrawerAdapter adapter) {
         mAdapterStack.push(adapter);
         setTitleAndSwitchToAdapter(adapter);
     }
@@ -108,7 +146,7 @@
     /**
      * Close the drawer if open.
      */
-    protected void closeDrawer() {
+    public void closeDrawer() {
         if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
             mDrawerLayout.closeDrawer(Gravity.LEFT);
         }
@@ -211,7 +249,7 @@
     }
 
     private void setTitleAndSwitchToAdapter(CarDrawerAdapter adapter) {
-        mToolbar.setTitle(adapter.getTitleResId());
+        setToolbarTitleFrom(adapter);
         // NOTE: We don't use swapAdapter() since different levels in the Drawer may switch between
         // car_menu_list_item_normal, car_menu_list_item_small and car_list_empty layouts.
         mDrawerList.getRecyclerView().setAdapter(adapter);
@@ -220,7 +258,8 @@
 
     private boolean maybeHandleUpClick() {
         if (mAdapterStack.size() > 1) {
-            mAdapterStack.pop();
+            CarDrawerAdapter adapter = mAdapterStack.pop();
+            adapter.cleanup();
             setTitleAndSwitchToAdapter(mAdapterStack.peek());
             return true;
         }
@@ -230,7 +269,8 @@
     /** Clears stack down to root adapter and switches to root adapter. */
     private void cleanupStackAndShowRoot() {
         while (mAdapterStack.size() > 1) {
-            mAdapterStack.pop();
+            CarDrawerAdapter adapter = mAdapterStack.pop();
+            adapter.cleanup();
         }
         setTitleAndSwitchToAdapter(mAdapterStack.peek());
     }
diff --git a/car-stream-ui-lib/src/com/android/car/app/CarDrawerAdapter.java b/car-stream-ui-lib/src/com/android/car/app/CarDrawerAdapter.java
index 166da14..1f2c04f 100644
--- a/car-stream-ui-lib/src/com/android/car/app/CarDrawerAdapter.java
+++ b/car-stream-ui-lib/src/com/android/car/app/CarDrawerAdapter.java
@@ -1,31 +1,84 @@
 package com.android.car.app;
 
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.StringRes;
 import android.support.car.ui.PagedListView;
 import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.car.stream.ui.R;
 
 /**
- * Base Adapter for displaying items in the CarDrawerActivity's Drawer which is a PagedListView.
+ * Base Adapter for displaying items in the CarDrawerActivity's Drawer which uses a PagedListView.
  * <p>
- * Implementors must return the string resource for the title that will be displayed when displaying
- * the contents of this adapter (see {@link #getTitleResId()}.
+ * Implementors must return the title that will be displayed when displaying the contents of the
+ * Drawer. They should either override {@link #getTitleResId()} or {@link #getTitleString()}. The
+ * title of the root-adapter will also be the main title showed in the toolbar when the drawer is
+ * closed.
  * <p>
  * This class also takes care of implementing the PageListView.ItemCamp contract and subclasses
  * should implement {@link #getActualItemCount()}.
  */
-public abstract class CarDrawerAdapter extends RecyclerView.Adapter<DrawerItemViewHolder>
-        implements PagedListView.ItemCap {
+public abstract class CarDrawerAdapter extends RecyclerView.Adapter<DrawerItemViewHolder> implements
+        PagedListView.ItemCap,
+        DrawerItemClickListener {
+    static final int INVALID_STRING_RES_ID = -1;
 
+    private final boolean mShowDisabledListOnEmpty;
+    private final boolean mUseSmallLayout;
+    private final Drawable mEmptyListDrawable;
     private int mMaxItems = -1;
 
+    protected CarDrawerAdapter(
+            Context context, boolean showDisabledListOnEmpty,boolean useSmallLayout) {
+        mShowDisabledListOnEmpty = showDisabledListOnEmpty;
+        mUseSmallLayout = useSmallLayout;
+        final int iconColor = context.getColor(R.color.car_tint);
+        mEmptyListDrawable = context.getDrawable(R.drawable.ic_list_view_disable);
+        mEmptyListDrawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
+    }
+
+    /**
+     * Subclasses should override this method or {@link #getTitleString()}.
+     *
+     * @return String resource to display in the toolbar title when displaying this adapter's
+     * contents.
+     */
+    @StringRes
+    protected int getTitleResId() {
+        return INVALID_STRING_RES_ID;
+    }
+
+    /**
+     * Subclasses should override this method or {@link #getTitleResId()}.
+     *
+     * @return String to display in the toolbar title when displaying this adapter's contents.
+     */
+    protected CharSequence getTitleString() {
+        return null;
+    }
+
+    // ItemCap implementation.
     @Override
     public final void setMaxItems(int maxItems) {
         mMaxItems = maxItems;
     }
 
+    private boolean shouldShowDisabledListItem() {
+        return mShowDisabledListOnEmpty && getActualItemCount() == 0;
+    }
+
+    // Honors ItemCap and mShowDisabledListOnEmpty.
     @Override
     public final int getItemCount() {
-        return mMaxItems >= 0  ? Math.min(mMaxItems, getActualItemCount()) : getActualItemCount();
+        if (shouldShowDisabledListItem()) {
+            return 1;
+        }
+        return mMaxItems >= 0 ? Math.min(mMaxItems, getActualItemCount()) : getActualItemCount();
     }
 
     /**
@@ -33,10 +86,43 @@
      */
     protected abstract int getActualItemCount();
 
+    @Override
+    public final int getItemViewType(int position) {
+        if (shouldShowDisabledListItem()) {
+            return R.layout.car_list_item_empty;
+        }
+        return mUseSmallLayout
+                ? R.layout.car_menu_list_item_small : R.layout.car_menu_list_item_normal;
+    }
+
+    @Override
+    public final DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
+        return new DrawerItemViewHolder(view);
+    }
+
+    @Override
+    public final void onBindViewHolder(DrawerItemViewHolder holder, int position) {
+        if (shouldShowDisabledListItem()) {
+            holder.getTitle().setText(null);
+            holder.getIcon().setImageDrawable(mEmptyListDrawable);
+            holder.setItemClickListener(null);
+        } else {
+            holder.setItemClickListener(this);
+            populateViewHolder(holder, position);
+        }
+    }
+
     /**
-     * @return String resource to display in the toolbar title when displaying this adapter's
-     * contents.
+     * Subclasses should set all elements in {@code holder} to populate the drawer-item.
+     * If some element is not used, it should be nulled out since these ViewHolder/View's are
+     * recycled.
      */
-    @StringRes
-    protected abstract int getTitleResId();
-}
+    protected abstract void populateViewHolder(DrawerItemViewHolder holder, int position);
+
+    /**
+     * Called when this adapter has been popped off the stack and is no longer needed. Subclasses
+     * can override to do any necessary cleanup.
+     */
+    protected void cleanup() {}
+}
\ No newline at end of file
diff --git a/car-stream-ui-lib/src/com/android/car/app/CarDrawerEmptyAdapter.java b/car-stream-ui-lib/src/com/android/car/app/CarDrawerEmptyAdapter.java
deleted file mode 100644
index 99c403b..0000000
--- a/car-stream-ui-lib/src/com/android/car/app/CarDrawerEmptyAdapter.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.car.app;
-
-import android.content.Context;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.StringRes;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.car.stream.ui.R;
-
-/**
- * Concrete subclass of {@link CarDrawerAdapter} to that displays a single "empty list" indicator.
- */
-public class CarDrawerEmptyAdapter extends CarDrawerAdapter {
-    @StringRes
-    private final int mTitleResId;
-    private final Drawable mEmptyListDrawable;
-
-    public CarDrawerEmptyAdapter(Context context, @StringRes int titleResId) {
-        mTitleResId = titleResId;
-        final int iconColor = context.getColor(R.color.car_tint);
-        mEmptyListDrawable = context.getDrawable(R.drawable.ic_list_view_disable);
-        mEmptyListDrawable.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
-    }
-
-    @Override
-    public DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View view = LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.car_list_item_empty, parent, false);
-        return new DrawerItemViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(DrawerItemViewHolder holder, int position) {
-        holder.getTitle().setText(null);
-        holder.getIcon().setImageDrawable(mEmptyListDrawable);
-        holder.setItemClickListener(null);
-    }
-
-    @Override
-    protected int getActualItemCount() {
-        return 1;
-    }
-
-    @Override
-    protected int getTitleResId() {
-        return mTitleResId;
-    }
-}
diff --git a/car-stream-ui-lib/src/com/android/car/app/CarDrawerListAdapter.java b/car-stream-ui-lib/src/com/android/car/app/CarDrawerListAdapter.java
deleted file mode 100644
index 7a2c052..0000000
--- a/car-stream-ui-lib/src/com/android/car/app/CarDrawerListAdapter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.android.car.app;
-
-import android.support.annotation.LayoutRes;
-import android.support.car.ui.PagedListView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.car.stream.ui.R;
-
-/**
- * Variant of {@link CarDrawerAdapter} for displaying a list of items that support clicks.
- * <p>
- * Subclasses should implement:
- * <ul>
- *     <li>{@link #populateViewHolder(DrawerItemViewHolder, int)} to actually populate the
- *     drawer-item.</li>
- *     <li>{@link #getTitleResId()} to set the string to display when the Drawer is displaying this
- *     adapter's contents.</li>
- *     <li>{@link #getActualItemCount()} to return the actual number of items in the adapter (since
- *     {@link #getItemCount()} needs to honor {@link PagedListView.ItemCap}.</li>
- *     <li>{@link #onItemClick(int)} to handle clicks on items. To load a sub-level of items, the
- *     handler may call {@link CarDrawerActivity#switchToAdapter(CarDrawerAdapter)} to load
- *     the next level of items.</li>
- * </ul>
- */
-public abstract class CarDrawerListAdapter extends CarDrawerAdapter
-        implements DrawerItemClickListener {
-    @LayoutRes
-    private final int mItemLayoutResId;
-
-    protected CarDrawerListAdapter(boolean useNormalLayout) {
-        mItemLayoutResId = useNormalLayout ?
-                R.layout.car_menu_list_item_normal : R.layout.car_menu_list_item_small;
-    }
-
-    @Override
-    public final DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View view = LayoutInflater.from(parent.getContext())
-                .inflate(mItemLayoutResId, parent, false);
-        return new DrawerItemViewHolder(view);
-    }
-
-    @Override
-    public final void onBindViewHolder(DrawerItemViewHolder holder, int position) {
-        holder.setItemClickListener(this);
-        populateViewHolder(holder, position);
-    }
-
-    /**
-     * Subclasses should set all elements in {@code holder} to populate the drawer-item.
-     * If some element is not used, it should be nulled out since these ViewHolder/View's are
-     * recycled.
-     */
-    protected abstract void populateViewHolder(DrawerItemViewHolder holder, int position);
-}
\ No newline at end of file
diff --git a/car-stream-ui-lib/src/com/android/car/app/DrawerItemViewHolder.java b/car-stream-ui-lib/src/com/android/car/app/DrawerItemViewHolder.java
index 55eedc5..e6ff1ef 100644
--- a/car-stream-ui-lib/src/com/android/car/app/DrawerItemViewHolder.java
+++ b/car-stream-ui-lib/src/com/android/car/app/DrawerItemViewHolder.java
@@ -79,8 +79,7 @@
     }
 
     /**
-     * Set click-listener on the view wrapped by this ViewHolder. For now only used by
-     * {@link CarDrawerListAdapter}.
+     * Set click-listener on the view wrapped by this ViewHolder.
      */
     void setItemClickListener(@Nullable DrawerItemClickListener listener) {
         if (listener != null) {