| // 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; |
| } |
| } |
| } |
| } |