Download Home : Enable multi-selection

Added ListItemView, GenericListItemView, PrefetchListItemView,
ImageListItemView classes to handle selection. Each of these
views will implement its own UI to handle selection.

Bug: 850600
Change-Id: I9707adb050b38c405ef153b2d4de9b93049ddd61
Reviewed-on: https://chromium-review.googlesource.com/1141159
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Reviewed-by: Theresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576208}
diff --git a/chrome/android/java/res/layout/download_manager_generic_item.xml b/chrome/android/java/res/layout/download_manager_generic_item.xml
index 1cde489..d159ece 100644
--- a/chrome/android/java/res/layout/download_manager_generic_item.xml
+++ b/chrome/android/java/res/layout/download_manager_generic_item.xml
@@ -4,52 +4,59 @@
      found in the LICENSE file.
 -->
 
-<android.support.v7.widget.GridLayout
+<org.chromium.chrome.browser.download.home.view.GenericListItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="64dp"
+    android:layout_height="match_parent"
     android:clickable="true"
-    android:background="@android:color/white"
-    app:columnCount="3"
-    app:rowCount="2">
-    <org.chromium.chrome.browser.widget.TintedImageView
-        android:id="@+id/thumbnail"
-        android:layout_width="@dimen/download_manager_generic_thumbnail_size"
-        android:layout_height="@dimen/download_manager_generic_thumbnail_size"
-        android:layout_marginStart="16dp"
-        android:layout_marginEnd="16dp"
-        android:scaleType="center"
-        app:layout_column="0"
-        app:layout_row="0"
-        app:layout_rowSpan="2"
-        app:layout_gravity="center_vertical"
-        app:chrometint="@color/dark_mode_tint" />
+    android:background="@color/modern_primary_color" >
 
-    <TextView
-        android:id="@+id/title"
-        style="@style/DownloadItemText"
-        android:layout_marginTop="11dp"
-        android:textAppearance="@style/BlackTitle1"
-        app:layout_column="1"
-        app:layout_row="0"
-        app:layout_gravity="fill_horizontal" />
-    <TextView
-        android:id="@+id/caption"
-        style="@style/DownloadItemText"
-        android:textAppearance="@style/BlackHint2"
-        app:layout_column="1"
-        app:layout_row="1"
-        app:layout_gravity="fill_horizontal" />
+    <android.support.v7.widget.GridLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="64dp"
+        android:background="@android:color/white"
+        app:columnCount="3"
+        app:rowCount="2">
 
-    <include layout="@layout/list_menu_button"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        app:layout_column="2"
-        app:layout_row="0"
-        app:layout_rowSpan="2"
-        app:layout_gravity="center_vertical" />
+        <org.chromium.chrome.browser.widget.TintedImageView
+            android:id="@+id/thumbnail"
+            android:layout_width="@dimen/download_manager_generic_thumbnail_size"
+            android:layout_height="@dimen/download_manager_generic_thumbnail_size"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="16dp"
+            android:scaleType="center"
+            app:layout_column="0"
+            app:layout_row="0"
+            app:layout_rowSpan="2"
+            app:layout_gravity="center_vertical"
+            app:chrometint="@color/dark_mode_tint" />
 
-</android.support.v7.widget.GridLayout>
\ No newline at end of file
+        <TextView
+            android:id="@+id/title"
+            style="@style/DownloadItemText"
+            android:layout_marginTop="11dp"
+            android:textAppearance="@style/BlackTitle1"
+            app:layout_column="1"
+            app:layout_row="0"
+            app:layout_gravity="fill_horizontal" />
+
+        <TextView
+            android:id="@+id/caption"
+            style="@style/DownloadItemText"
+            android:textAppearance="@style/BlackHint2"
+            app:layout_column="1"
+            app:layout_row="1"
+            app:layout_gravity="fill_horizontal" />
+
+        <include layout="@layout/list_menu_button"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            app:layout_column="2"
+            app:layout_row="0"
+            app:layout_rowSpan="2"
+            app:layout_gravity="center_vertical" />
+    </android.support.v7.widget.GridLayout>
+</org.chromium.chrome.browser.download.home.view.GenericListItemView>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/download_manager_image_item.xml b/chrome/android/java/res/layout/download_manager_image_item.xml
index 6fb4abb5..20cc082 100644
--- a/chrome/android/java/res/layout/download_manager_image_item.xml
+++ b/chrome/android/java/res/layout/download_manager_image_item.xml
@@ -4,12 +4,21 @@
      found in the LICENSE file.
 -->
 
-<ImageView
+<org.chromium.chrome.browser.download.home.view.ImageListItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/thumbnail"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:scaleType="centerCrop"
-    android:layout_gravity="center_vertical"
-    tools:ignore="ContentDescription" />
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clickable="true"
+    android:background="@color/modern_primary_color" >
+
+    <ImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:scaleType="centerCrop"
+        android:layout_gravity="center_vertical"
+        tools:ignore="ContentDescription" />
+
+</org.chromium.chrome.browser.download.home.view.ImageListItemView>
diff --git a/chrome/android/java/res/layout/download_manager_prefetch_item.xml b/chrome/android/java/res/layout/download_manager_prefetch_item.xml
index 27ee4a40..ef371e2 100644
--- a/chrome/android/java/res/layout/download_manager_prefetch_item.xml
+++ b/chrome/android/java/res/layout/download_manager_prefetch_item.xml
@@ -4,95 +4,101 @@
      found in the LICENSE file.
 -->
 
-<android.support.v7.widget.GridLayout
+<org.chromium.chrome.browser.download.home.view.PrefetchListItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="114dp"
-    android:clickable="true"
-    android:background="@android:color/white"
-    app:columnCount="4"
-    app:rowCount="4">
-    <ImageView
-        android:id="@+id/thumbnail"
-        android:layout_width="114dp"
+    android:layout_height="match_parent"
+    android:clickable="true" >
+
+    <android.support.v7.widget.GridLayout
+        android:layout_width="match_parent"
         android:layout_height="114dp"
-        android:layout_marginEnd="9dp"
-        android:scaleType="fitCenter"
-        android:background="@color/google_grey_100"
-        app:layout_column="0"
-        app:layout_row="0"
-        app:layout_rowSpan="4"
-        tools:ignore="ContentDescription" />
+        android:background="@android:color/white"
+        app:columnCount="4"
+        app:rowCount="4">
 
-    <Space
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        app:layout_gravity="fill"
-        app:layout_column="0"
-        app:layout_row="3"
-        app:layout_columnSpan="4" />
+        <ImageView
+            android:id="@+id/thumbnail"
+            android:layout_width="114dp"
+            android:layout_height="114dp"
+            android:layout_marginEnd="9dp"
+            android:scaleType="fitCenter"
+            android:background="@color/google_grey_100"
+            app:layout_column="0"
+            app:layout_row="0"
+            app:layout_rowSpan="4"
+            tools:ignore="ContentDescription" />
 
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="12dp"
-        android:minHeight="40dp"
-        android:maxLines="2"
-        android:ellipsize="end"
-        android:textAppearance="@style/BlackBodyDefault"
-        android:textAlignment="viewStart"
-        app:layout_column="1"
-        app:layout_row="0"
-        app:layout_columnSpan="2"
-        app:layout_gravity="fill_horizontal" />
+        <Space
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            app:layout_gravity="fill"
+            app:layout_column="0"
+            app:layout_row="3"
+            app:layout_columnSpan="4" />
 
-    <TextView
-        android:id="@+id/caption"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:layout_marginBottom="8dp"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:textAppearance="@style/BlackCaption"
-        android:textAlignment="viewStart"
-        app:layout_column="1"
-        app:layout_row="1"
-        app:layout_columnSpan="3"
-        app:layout_gravity="fill_horizontal" />
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dp"
+            android:minHeight="40dp"
+            android:maxLines="2"
+            android:ellipsize="end"
+            android:textAppearance="@style/BlackBodyDefault"
+            android:textAlignment="viewStart"
+            app:layout_column="1"
+            app:layout_row="0"
+            app:layout_columnSpan="2"
+            app:layout_gravity="fill_horizontal" />
 
-    <TextView
-        android:id="@+id/timestamp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:textAppearance="@style/BlackCaption"
-        android:textAlignment="viewStart"
-        app:layout_column="1"
-        app:layout_row="2"
-        app:layout_gravity="center_vertical" />
+        <TextView
+            android:id="@+id/caption"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            android:layout_marginBottom="8dp"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:textAppearance="@style/BlackCaption"
+            android:textAlignment="viewStart"
+            app:layout_column="1"
+            app:layout_row="1"
+            app:layout_columnSpan="3"
+            app:layout_gravity="fill_horizontal" />
 
-    <ImageView
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:layout_marginStart="4dp"
-        android:src="@drawable/offline_pin_round"
-        android:scaleType="centerInside"
-        android:tint="@color/default_icon_color"
-        app:layout_column="2"
-        app:layout_row="2"
-        app:layout_columnSpan="2"
-        app:layout_gravity="start|center_vertical"
-        tools:ignore="ContentDescription" />
+        <TextView
+            android:id="@+id/timestamp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:textAppearance="@style/BlackCaption"
+            android:textAlignment="viewStart"
+            app:layout_column="1"
+            app:layout_row="2"
+            app:layout_gravity="center_vertical" />
 
-    <include layout="@layout/list_menu_button"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:paddingTop="12dp"
-        app:layout_column="3"
-        app:layout_row="0" />
-</android.support.v7.widget.GridLayout>
\ No newline at end of file
+        <ImageView
+            android:layout_width="12dp"
+            android:layout_height="12dp"
+            android:layout_marginStart="4dp"
+            android:src="@drawable/offline_pin_round"
+            android:scaleType="centerInside"
+            android:tint="@color/default_icon_color"
+            app:layout_column="2"
+            app:layout_row="2"
+            app:layout_columnSpan="2"
+            app:layout_gravity="start|center_vertical"
+            tools:ignore="ContentDescription" />
+
+        <include layout="@layout/list_menu_button"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:paddingTop="12dp"
+                app:layout_column="3"
+                app:layout_row="0" />
+    </android.support.v7.widget.GridLayout>
+</org.chromium.chrome.browser.download.home.view.PrefetchListItemView>
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
index e858aa5..bf72126 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
@@ -63,7 +63,7 @@
         mSelectionDelegate = new SelectionDelegate<ListItem>();
         mListCoordinator = new DateOrderedListCoordinator(mActivity, offTheRecord,
                 OfflineContentAggregatorFactory.forProfile(profile),
-                mDeleteCoordinator::showSnackbar, this ::notifyFilterChanged);
+                mDeleteCoordinator::showSnackbar, mSelectionDelegate, this ::notifyFilterChanged);
 
         mMainView =
                 (ViewGroup) LayoutInflater.from(mActivity).inflate(R.layout.download_main, null);
@@ -138,6 +138,7 @@
     }
 
     private void notifyFilterChanged(@FilterType int filter) {
+        mSelectionDelegate.clearSelection();
         if (mMuteFilterChanges) return;
 
         String url = Filters.toUrl(filter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
index 0b006c8a..6f2f7fd4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListCoordinator.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.browser.download.home.filter.FilterCoordinator;
 import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
 import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.components.offline_items_collection.OfflineContentProvider;
 import org.chromium.components.offline_items_collection.OfflineItem;
 
@@ -58,11 +59,13 @@
      */
     public DateOrderedListCoordinator(Context context, Boolean offTheRecord,
             OfflineContentProvider provider, DeleteController deleteController,
+            SelectionDelegate<ListItem> selectionDelegate,
             FilterCoordinator.Observer filterObserver) {
         ListItemModel model = new ListItemModel();
         DecoratedListItemModel decoratedModel = new DecoratedListItemModel(model);
         mView = new DateOrderedListView(context, decoratedModel);
-        mMediator = new DateOrderedListMediator(offTheRecord, provider, deleteController, model);
+        mMediator = new DateOrderedListMediator(
+                offTheRecord, provider, deleteController, selectionDelegate, model);
 
         // Hook up the FilterCoordinator with our mediator.
         mFilterCoordinator = new FilterCoordinator(context, mMediator.getFilterSource());
@@ -90,4 +93,4 @@
     public void setSelectedFilter(@FilterType int filter) {
         mFilterCoordinator.setSelectedFilter(filter);
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
index 8abf9a2..95bef20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListMediator.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.widget.ThumbnailProvider;
 import org.chromium.chrome.browser.widget.ThumbnailProvider.ThumbnailRequest;
 import org.chromium.chrome.browser.widget.ThumbnailProviderImpl;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.components.offline_items_collection.OfflineContentProvider;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.VisualsCallback;
@@ -59,7 +60,8 @@
      * @param model            The {@link ListItemModel} to push {@code provider} into.
      */
     public DateOrderedListMediator(boolean offTheRecord, OfflineContentProvider provider,
-            DeleteController deleteController, ListItemModel model) {
+            DeleteController deleteController, SelectionDelegate<ListItem> selectionDelegate,
+            ListItemModel model) {
         // Build a chain from the data source to the model.  The chain will look like:
         // [OfflineContentProvider] ->
         //     [OfflineItemSource] ->
@@ -92,6 +94,7 @@
         mModel.getProperties().setShareCallback(item -> {});
         mModel.getProperties().setRemoveCallback(this::onDeleteItem);
         mModel.getProperties().setVisualsProvider(this::getVisuals);
+        mModel.getProperties().setSelectionDelegate(selectionDelegate);
     }
 
     /** Tears down this mediator. */
@@ -179,4 +182,4 @@
             mHandler.post(() -> mModel.getProperties().setEnableItemAnimations(true));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
index 07899c2d..c24216f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
@@ -7,9 +7,6 @@
 import android.support.annotation.CallSuper;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
-import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.AppCompatTextView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.LayoutInflater;
@@ -26,10 +23,11 @@
 import org.chromium.chrome.browser.download.home.list.ListItem.DateListItem;
 import org.chromium.chrome.browser.download.home.list.ListItem.OfflineItemListItem;
 import org.chromium.chrome.browser.download.home.list.ListItem.ViewListItem;
+import org.chromium.chrome.browser.download.home.view.GenericListItemView;
+import org.chromium.chrome.browser.download.home.view.ListItemView;
 import org.chromium.chrome.browser.widget.ListMenuButton;
 import org.chromium.chrome.browser.widget.ListMenuButton.Item;
 import org.chromium.chrome.browser.widget.TintedImageButton;
-import org.chromium.chrome.browser.widget.TintedImageView;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemState;
@@ -208,7 +206,6 @@
     public static class GenericViewHolder extends ThumbnailAwareViewHolder {
         private final TextView mTitle;
         private final TextView mCaption;
-        private final TintedImageView mThumbnail;
 
         /**
          * Whether or not we are currently showing an icon.  This determines whether or not we
@@ -233,7 +230,6 @@
 
             mTitle = (TextView) itemView.findViewById(R.id.title);
             mCaption = (TextView) itemView.findViewById(R.id.caption);
-            mThumbnail = (TintedImageView) itemView.findViewById(R.id.thumbnail);
         }
 
         // ListItemViewHolder implementation.
@@ -245,41 +241,22 @@
             mTitle.setText(offlineItem.item.title);
             mCaption.setText(UiUtils.generateGenericCaption(offlineItem.item));
 
-            itemView.setOnClickListener(
-                    v -> properties.getOpenCallback().onResult(offlineItem.item));
-
             mIconId = UiUtils.getIconForItem(offlineItem.item);
-
-            if (mDrawingIcon) setThumbnailToIcon();
         }
 
         @Override
         void onVisualsChanged(ImageView view, OfflineItemVisuals visuals) {
             mDrawingIcon = visuals == null || visuals.icon == null;
 
+            GenericListItemView selectableView = (GenericListItemView) itemView;
             if (mDrawingIcon) {
-                setThumbnailToIcon();
+                if (mIconId != INVALID_ID) {
+                    selectableView.setThumbnailResource(mIconId);
+                }
             } else {
-                mThumbnail.setBackground(null);
-                mThumbnail.setTint(null);
-
-                RoundedBitmapDrawable drawable =
-                        RoundedBitmapDrawableFactory.create(view.getResources(), visuals.icon);
-                drawable.setCircular(true);
-                mThumbnail.setImageDrawable(drawable);
+                selectableView.setThumbnail(visuals.icon);
             }
         }
-
-        private void setThumbnailToIcon() {
-            if (mIconId == INVALID_ID) return;
-
-            mThumbnail.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
-            mThumbnail.getBackground().setLevel(
-                    itemView.getResources().getInteger(R.integer.list_item_level_default));
-            mThumbnail.setImageResource(mIconId);
-            mThumbnail.setTint(AppCompatResources.getColorStateList(
-                    itemView.getContext(), R.color.default_icon_color_blue));
-        }
     }
 
     /**
@@ -319,9 +296,6 @@
             OfflineItemListItem offlineItem = (OfflineItemListItem) item;
             View imageView = itemView.findViewById(R.id.thumbnail);
             imageView.setContentDescription(offlineItem.item.title);
-
-            itemView.setOnClickListener(
-                    v -> properties.getOpenCallback().onResult(offlineItem.item));
         }
 
         @Override
@@ -365,9 +339,6 @@
             mTitle.setText(offlineItem.item.title);
             mCaption.setText(UiUtils.generatePrefetchCaption(offlineItem.item));
             mTimestamp.setText(UiUtils.generatePrefetchTimestamp(offlineItem.date));
-
-            itemView.setOnClickListener(
-                    v -> properties.getOpenCallback().onResult(offlineItem.item));
         }
 
         @Override
@@ -480,6 +451,12 @@
             // If we're rebinding the same item, ignore the bind.
             if (offlineItem.id.equals(mId)) return;
 
+            ListItemView selectableView = (ListItemView) itemView;
+            selectableView.setSelectionDelegate(properties.getSelectionDelegate());
+            selectableView.setItem(item);
+            selectableView.setClickCallback(
+                    () -> properties.getOpenCallback().onResult(offlineItem));
+
             // Clear any associated bitmap from the thumbnail.
             if (mId != null) onVisualsChanged(mThumbnail, null);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyModel.java
index 98aa197..1ddd249 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListPropertyModel.java
@@ -6,6 +6,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.modelutil.PropertyObservable;
+import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemVisuals;
 import org.chromium.components.offline_items_collection.VisualsCallback;
@@ -55,6 +56,7 @@
     private Callback<OfflineItem> mShareCallback;
     private Callback<OfflineItem> mRemoveCallback;
     private VisualsProvider mVisualsProvider;
+    private SelectionDelegate<ListItem> mSelectionDelegate;
 
     /** Sets whether or not item animations should be enabled. */
     public void setEnableItemAnimations(boolean enableItemAnimations) {
@@ -151,4 +153,14 @@
     public VisualsProvider getVisualsProvider() {
         return mVisualsProvider;
     }
-}
\ No newline at end of file
+
+    /** Sets the selection delegate to handle selection of list items. */
+    public void setSelectionDelegate(SelectionDelegate<ListItem> selectionDelegate) {
+        mSelectionDelegate = selectionDelegate;
+    }
+
+    /** @return The selection delegate to handle multiple item selections. */
+    public SelectionDelegate<ListItem> getSelectionDelegate() {
+        return mSelectionDelegate;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/GenericListItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/GenericListItemView.java
new file mode 100644
index 0000000..66ee464
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/GenericListItemView.java
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.view;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.support.v7.content.res.AppCompatResources;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.widget.TintedImageView;
+
+/**
+ * Represents a completed download in the download home.
+ */
+public class GenericListItemView extends ListItemView {
+    private TintedImageView mThumbnail;
+    private final ColorStateList mCheckedIconForegroundColorList;
+    private final AnimatedVectorDrawableCompat mCheckDrawable;
+
+    public GenericListItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mCheckDrawable = AnimatedVectorDrawableCompat.create(
+                getContext(), R.drawable.ic_check_googblue_24dp_animated);
+        mCheckedIconForegroundColorList = DownloadUtils.getIconForegroundColorList(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mThumbnail = (TintedImageView) findViewById(R.id.thumbnail);
+    }
+
+    @Override
+    protected void updateView() {
+        if (isChecked()) {
+            mThumbnail.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
+            mThumbnail.getBackground().setLevel(
+                    getResources().getInteger(R.integer.list_item_level_selected));
+
+            mThumbnail.setImageDrawable(mCheckDrawable);
+            mThumbnail.setTint(mCheckedIconForegroundColorList);
+            mCheckDrawable.start();
+        } else if (mThumbnailBitmap != null) {
+            assert !mThumbnailBitmap.isRecycled();
+
+            mThumbnail.setBackground(null);
+            mThumbnail.setTint(null);
+
+            RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(
+                    mThumbnail.getResources(), mThumbnailBitmap);
+            drawable.setCircular(true);
+            mThumbnail.setImageDrawable(drawable);
+        } else {
+            mThumbnail.setBackgroundResource(R.drawable.list_item_icon_modern_bg);
+            mThumbnail.getBackground().setLevel(
+                    mThumbnail.getResources().getInteger(R.integer.list_item_level_default));
+            mThumbnail.setImageResource(mIconResId);
+            mThumbnail.setTint(AppCompatResources.getColorStateList(
+                    mThumbnail.getContext(), R.color.dark_mode_tint));
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ImageListItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ImageListItemView.java
new file mode 100644
index 0000000..926e454
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ImageListItemView.java
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * Represents an image in the download home.
+ */
+public class ImageListItemView extends ListItemView {
+    public ImageListItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ListItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ListItemView.java
new file mode 100644
index 0000000..29ce1c8
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/ListItemView.java
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.view;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.browser.download.home.list.ListItem;
+import org.chromium.chrome.browser.widget.selection.SelectableItemViewBase;
+
+/** A class that represents a variety of possible list items to show in downloads home. */
+public abstract class ListItemView extends SelectableItemViewBase<ListItem> {
+    private Runnable mClickCallback;
+    protected Bitmap mThumbnailBitmap;
+    protected int mIconResId;
+
+    public ListItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setClickCallback(Runnable listener) {
+        mClickCallback = listener;
+    }
+
+    @Override
+    protected void onClick() {
+        mClickCallback.run();
+    }
+
+    public void setThumbnail(Bitmap thumbnail) {
+        mThumbnailBitmap = thumbnail;
+        updateView();
+    }
+
+    public void setThumbnailResource(int iconResId) {
+        mIconResId = iconResId;
+        updateView();
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/PrefetchListItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/PrefetchListItemView.java
new file mode 100644
index 0000000..3da25ed
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/view/PrefetchListItemView.java
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.download.home.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * Represents a prefetched page in the download home.
+ */
+public class PrefetchListItemView extends ListItemView {
+    public PrefetchListItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 6d613493..e172677 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -479,6 +479,10 @@
   "java/src/org/chromium/chrome/browser/download/home/list/ItemUtils.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListItem.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListItemModel.java",
+  "java/src/org/chromium/chrome/browser/download/home/view/GenericListItemView.java",
+  "java/src/org/chromium/chrome/browser/download/home/view/ImageListItemView.java",
+  "java/src/org/chromium/chrome/browser/download/home/view/ListItemView.java",
+  "java/src/org/chromium/chrome/browser/download/home/view/PrefetchListItemView.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListPropertyModel.java",
   "java/src/org/chromium/chrome/browser/download/home/list/ListPropertyViewBinder.java",