1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.media.browse; 18 19 import androidx.annotation.Nullable; 20 import androidx.recyclerview.widget.GridLayoutManager; 21 import androidx.recyclerview.widget.RecyclerView; 22 23 import com.android.car.media.R; 24 import com.android.car.media.common.MediaItemMetadata; 25 import com.android.car.ui.recyclerview.DelegatingContentLimitingAdapter; 26 27 import java.util.List; 28 29 /** 30 * Provides list limiting functionality to {@link BrowseAdapter}. 31 */ 32 public class LimitedBrowseAdapter extends DelegatingContentLimitingAdapter<BrowseViewHolder> { 33 34 private final BrowseAdapter mBrowseAdapter; 35 private final GridLayoutManager mLayoutManager; 36 private final int mMaxSpanSize; 37 38 @Nullable private String mAnchorId; 39 LimitedBrowseAdapter(BrowseAdapter browseAdapter, GridLayoutManager manager, BrowseAdapter.Observer browseAdapterObserver)40 public LimitedBrowseAdapter(BrowseAdapter browseAdapter, GridLayoutManager manager, 41 BrowseAdapter.Observer browseAdapterObserver) { 42 super(browseAdapter, R.id.browse_list_uxr_config); 43 44 mBrowseAdapter = browseAdapter; 45 mLayoutManager = manager; 46 mMaxSpanSize = manager.getSpanCount(); 47 48 mLayoutManager.setSpanSizeLookup(mSpanSizeLookup); 49 mBrowseAdapter.registerObserver(browseAdapterObserver); 50 mBrowseAdapter.setOnListChangedListener(((previousList, currentList) -> { 51 updateUnderlyingDataChanged(currentList.size(), validateAnchor()); 52 })); 53 } 54 55 private final GridLayoutManager.SpanSizeLookup mSpanSizeLookup = 56 new GridLayoutManager.SpanSizeLookup() { 57 @Override 58 public int getSpanSize(int position) { 59 if (getItemViewType(position) == getScrollingLimitedMessageViewType()) { 60 return mMaxSpanSize; 61 } 62 63 int itemIndex = positionToIndex(position); 64 return mBrowseAdapter.getSpanSize(itemIndex, mMaxSpanSize); 65 } 66 }; 67 68 /** 69 * @see BrowseAdapter#submitItems(MediaItemMetadata, List) 70 */ submitItems(@ullable MediaItemMetadata parentItem, @Nullable List<MediaItemMetadata> items)71 public void submitItems(@Nullable MediaItemMetadata parentItem, 72 @Nullable List<MediaItemMetadata> items) { 73 mBrowseAdapter.submitItems(parentItem, items); 74 75 if (items == null) { 76 updateUnderlyingDataChanged(0, 0); 77 return; 78 } 79 // We can't take any action with the new items as they must first go through the 80 // AsyncListDiffer of ListAdapter. This is handled in the OnListChangedListener. 81 } 82 validateAnchor()83 private int validateAnchor() { 84 if (mAnchorId == null) { 85 return 0; 86 } 87 88 List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); 89 for (int i = 0; i < items.size(); i++) { 90 MediaItemMetadata mediaItem = items.get(i).mMediaItem; 91 if (mediaItem != null && mAnchorId.equals(mediaItem.getId())) { 92 return i; 93 } 94 } 95 96 // The anchor isn't present in the new list, reset it. 97 mAnchorId = null; 98 return 0; 99 } 100 101 102 @Override computeAnchorIndexWhenRestricting()103 public int computeAnchorIndexWhenRestricting() { 104 List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); 105 if (items.size() <= 0) { 106 mAnchorId = null; 107 return 0; 108 } 109 110 int anchorIndex = (getFirstVisibleItemPosition() + getLastVisibleItemPosition()) / 2; 111 if (0 <= anchorIndex && anchorIndex < items.size()) { 112 MediaItemMetadata mediaItem = items.get(anchorIndex).mMediaItem; 113 mAnchorId = mediaItem != null ? mediaItem.getId() : null; 114 return anchorIndex; 115 } else { 116 mAnchorId = null; 117 return 0; 118 } 119 } 120 getFirstVisibleItemPosition()121 private int getFirstVisibleItemPosition() { 122 int firstItem = mLayoutManager.findFirstCompletelyVisibleItemPosition(); 123 if (firstItem == RecyclerView.NO_POSITION) { 124 firstItem = mLayoutManager.findFirstVisibleItemPosition(); 125 } 126 return firstItem; 127 } 128 getLastVisibleItemPosition()129 private int getLastVisibleItemPosition() { 130 int lastItem = mLayoutManager.findLastCompletelyVisibleItemPosition(); 131 if (lastItem == RecyclerView.NO_POSITION) { 132 lastItem = mLayoutManager.findLastVisibleItemPosition(); 133 } 134 return lastItem; 135 } 136 } 137