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) {