blob: 30e035439acc539c8aeacb5f60f5fd17d92356dd [file] [log] [blame]
// 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.list;
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.RecyclerView.Recycler;
import android.support.v7.widget.RecyclerView.State;
import android.view.View;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.download.home.list.DateOrderedListCoordinator.DateOrderedListObserver;
import org.chromium.chrome.browser.download.home.list.holder.ListItemViewHolder;
import org.chromium.chrome.browser.modelutil.ForwardingListObservable;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
/**
* The View component of a DateOrderedList. This takes the DateOrderedListModel and creates the
* glue to display it on the screen.
*/
class DateOrderedListView {
private final DecoratedListItemModel mModel;
private final int mImageWidthPx;
private final int mImagePaddingPx;
private final int mPrefetchVerticalPaddingPx;
private final int mPrefetchHorizontalPaddingPx;
private final RecyclerView mView;
private static class ModelChangeProcessor extends ForwardingListObservable<Void>
implements RecyclerViewAdapter.Delegate<ListItemViewHolder, Void> {
private final DecoratedListItemModel mModel;
public ModelChangeProcessor(DecoratedListItemModel model) {
mModel = model;
model.addObserver(this);
}
@Override
public int getItemCount() {
return mModel.size();
}
@Override
public int getItemViewType(int position) {
return ListUtils.getViewTypeForItem(mModel.get(position));
}
@Override
public void onBindViewHolder(
ListItemViewHolder viewHolder, int position, @Nullable Void payload) {
viewHolder.bind(mModel.getProperties(), mModel.get(position));
}
}
/** Creates an instance of a {@link DateOrderedListView} representing {@code model}. */
public DateOrderedListView(Context context, DecoratedListItemModel model,
DateOrderedListObserver dateOrderedListObserver) {
mModel = model;
mImageWidthPx =
context.getResources().getDimensionPixelSize(R.dimen.download_manager_image_width);
mImagePaddingPx = context.getResources().getDimensionPixelOffset(
R.dimen.download_manager_image_padding);
mPrefetchHorizontalPaddingPx = context.getResources().getDimensionPixelSize(
R.dimen.download_manager_prefetch_horizontal_margin);
mPrefetchVerticalPaddingPx = context.getResources().getDimensionPixelSize(
R.dimen.download_manager_prefetch_vertical_margin);
mView = new RecyclerView(context);
mView.setHasFixedSize(true);
mView.getItemAnimator().setChangeDuration(0);
mView.getItemAnimator().setMoveDuration(0);
mView.setLayoutManager(new GridLayoutManagerImpl(context));
mView.addItemDecoration(new ItemDecorationImpl());
PropertyModelChangeProcessor.create(
mModel.getProperties(), mView, new ListPropertyViewBinder());
// Do the final hook up to the underlying data adapter.
DateOrderedListViewAdapter adapter = new DateOrderedListViewAdapter(
mModel, new ModelChangeProcessor(mModel), ListItemViewHolder::create);
mView.setAdapter(adapter);
mView.post(adapter::notifyDataSetChanged);
mView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
dateOrderedListObserver.onListScroll(mView.canScrollVertically(-1));
}
});
}
/** @return The Android {@link View} representing this widget. */
public View getView() {
return mView;
}
private class GridLayoutManagerImpl extends GridLayoutManager {
/** Creates an instance of a {@link GridLayoutManagerImpl}. */
public GridLayoutManagerImpl(Context context) {
super(context, 1 /* spanCount */, VERTICAL, false /* reverseLayout */);
setSpanSizeLookup(new SpanSizeLookupImpl());
}
// GridLayoutManager implementation.
@Override
public void onLayoutChildren(Recycler recycler, State state) {
assert getOrientation() == VERTICAL;
int availableWidth = getWidth() - mImagePaddingPx;
int columnWidth = mImageWidthPx - mImagePaddingPx;
int easyFitSpan = availableWidth / columnWidth;
double remaining =
((double) (availableWidth - easyFitSpan * columnWidth)) / columnWidth;
if (remaining > 0.5) easyFitSpan++;
setSpanCount(Math.max(1, easyFitSpan));
super.onLayoutChildren(recycler, state);
}
private class SpanSizeLookupImpl extends SpanSizeLookup {
// SpanSizeLookup implementation.
@Override
public int getSpanSize(int position) {
return ListUtils.getSpanSize(mModel.get(position), getSpanCount());
}
}
}
private class ItemDecorationImpl extends ItemDecoration {
// ItemDecoration implementation.
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
int position = parent.getChildAdapterPosition(view);
if (position < 0 || position >= mModel.size()) return;
switch (ListUtils.getViewTypeForItem(mModel.get(position))) {
case ListUtils.ViewType.IMAGE:
outRect.left = mImagePaddingPx;
outRect.right = mImagePaddingPx;
outRect.top = mImagePaddingPx;
outRect.bottom = mImagePaddingPx;
break;
case ListUtils.ViewType.VIDEO: // Intentional fallthrough.
case ListUtils.ViewType.IN_PROGRESS_VIDEO:
outRect.left = mPrefetchHorizontalPaddingPx;
outRect.right = mPrefetchHorizontalPaddingPx;
outRect.bottom = mPrefetchHorizontalPaddingPx;
break;
case ListUtils.ViewType.PREFETCH:
outRect.left = mPrefetchHorizontalPaddingPx;
outRect.right = mPrefetchHorizontalPaddingPx;
outRect.top = mPrefetchVerticalPaddingPx / 2;
outRect.bottom = mPrefetchVerticalPaddingPx / 2;
break;
}
}
}
}