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.CarUiGridLayoutStyle; 26 import com.android.car.ui.recyclerview.CarUiRecyclerView; 27 import com.android.car.ui.recyclerview.DelegatingContentLimitingAdapter; 28 29 import java.util.List; 30 31 /** 32 * Provides list limiting functionality to {@link BrowseAdapter}. 33 */ 34 public class LimitedBrowseAdapter extends DelegatingContentLimitingAdapter<BrowseViewHolder> { 35 36 private final CarUiRecyclerView mRecyclerView; 37 private final BrowseAdapter mBrowseAdapter; 38 private final int mMaxSpanSize; 39 40 @Nullable private String mAnchorId; 41 LimitedBrowseAdapter(CarUiRecyclerView recyclerView, BrowseAdapter browseAdapter, BrowseAdapter.Observer browseAdapterObserver)42 public LimitedBrowseAdapter(CarUiRecyclerView recyclerView, BrowseAdapter browseAdapter, 43 BrowseAdapter.Observer browseAdapterObserver) { 44 super(browseAdapter, R.id.browse_list_uxr_config); 45 46 mRecyclerView = recyclerView; 47 mBrowseAdapter = browseAdapter; 48 49 CarUiGridLayoutStyle layoutStyle = (CarUiGridLayoutStyle) mRecyclerView.getLayoutStyle(); 50 mMaxSpanSize = layoutStyle.getSpanCount(); 51 layoutStyle.setSpanSizeLookup(mSpanSizeLookup); 52 mRecyclerView.setLayoutStyle(layoutStyle); 53 mBrowseAdapter.registerObserver(browseAdapterObserver); 54 mBrowseAdapter.setOnListChangedListener(((previousList, currentList) -> { 55 updateUnderlyingDataChanged(currentList.size(), validateAnchor()); 56 })); 57 } 58 59 private final GridLayoutManager.SpanSizeLookup mSpanSizeLookup = 60 new GridLayoutManager.SpanSizeLookup() { 61 @Override 62 public int getSpanSize(int position) { 63 if (getItemViewType(position) == getScrollingLimitedMessageViewType()) { 64 return mMaxSpanSize; 65 } 66 67 int itemIndex = positionToIndex(position); 68 return mBrowseAdapter.getSpanSize(itemIndex, mMaxSpanSize); 69 } 70 }; 71 72 /** 73 * @see BrowseAdapter#submitItems(MediaItemMetadata, List) 74 */ submitItems(@ullable MediaItemMetadata parentItem, @Nullable List<MediaItemMetadata> items)75 public void submitItems(@Nullable MediaItemMetadata parentItem, 76 @Nullable List<MediaItemMetadata> items) { 77 mBrowseAdapter.submitItems(parentItem, items); 78 79 if (items == null) { 80 updateUnderlyingDataChanged(0, 0); 81 return; 82 } 83 // We can't take any action with the new items as they must first go through the 84 // AsyncListDiffer of ListAdapter. This is handled in the OnListChangedListener. 85 } 86 validateAnchor()87 private int validateAnchor() { 88 if (mAnchorId == null) { 89 return 0; 90 } 91 92 List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); 93 for (int i = 0; i < items.size(); i++) { 94 MediaItemMetadata mediaItem = items.get(i).mMediaItem; 95 if (mediaItem != null && mAnchorId.equals(mediaItem.getId())) { 96 return i; 97 } 98 } 99 100 // The anchor isn't present in the new list, reset it. 101 mAnchorId = null; 102 return 0; 103 } 104 105 106 @Override computeAnchorIndexWhenRestricting()107 public int computeAnchorIndexWhenRestricting() { 108 List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); 109 if (items.size() <= 0) { 110 mAnchorId = null; 111 return 0; 112 } 113 114 int anchorIndex = (getFirstVisibleItemPosition() + getLastVisibleItemPosition()) / 2; 115 if (0 <= anchorIndex && anchorIndex < items.size()) { 116 MediaItemMetadata mediaItem = items.get(anchorIndex).mMediaItem; 117 mAnchorId = mediaItem != null ? mediaItem.getId() : null; 118 return anchorIndex; 119 } else { 120 mAnchorId = null; 121 return 0; 122 } 123 } 124 getFirstVisibleItemPosition()125 private int getFirstVisibleItemPosition() { 126 int firstItem = mRecyclerView.findFirstCompletelyVisibleItemPosition(); 127 if (firstItem == RecyclerView.NO_POSITION) { 128 firstItem = mRecyclerView.findFirstVisibleItemPosition(); 129 } 130 return firstItem; 131 } 132 getLastVisibleItemPosition()133 private int getLastVisibleItemPosition() { 134 int lastItem = mRecyclerView.findLastCompletelyVisibleItemPosition(); 135 if (lastItem == RecyclerView.NO_POSITION) { 136 lastItem = mRecyclerView.findLastVisibleItemPosition(); 137 } 138 return lastItem; 139 } 140 } 141