diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-09-17 02:57:05 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-09-17 02:57:05 +0000 |
commit | 726305c4941c965aecde4f15dacc354e9d939ec2 (patch) | |
tree | c0c71c3bc8f6e93ca9fbc9ee4915bf5392bae77e | |
parent | a92dded71d7a4e9f579586c6ae40db5512e5e4c1 (diff) | |
parent | 9a7891eaadb7c9d3e7bfc5cdaccd065e8d72a820 (diff) | |
download | Media-726305c4941c965aecde4f15dacc354e9d939ec2.tar.gz |
Merge changes from topic "2020-09-14-spans" into rvc-qpr-dev
* changes:
Fix span computation
Integrate the list limit updates
4 files changed, 180 insertions, 35 deletions
diff --git a/src/com/android/car/media/BrowseViewController.java b/src/com/android/car/media/BrowseViewController.java index 5f7be28..cd67f71 100644 --- a/src/com/android/car/media/BrowseViewController.java +++ b/src/com/android/car/media/BrowseViewController.java @@ -20,7 +20,6 @@ import static com.android.car.arch.common.LiveDataFunctions.ifThenElse; import android.car.content.pm.CarPackageManager; import android.content.Context; -import android.content.res.Resources; import android.os.Handler; import android.text.TextUtils; import android.util.Log; @@ -41,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.car.apps.common.util.ViewUtils; import com.android.car.arch.common.FutureData; import com.android.car.media.browse.BrowseAdapter; +import com.android.car.media.browse.LimitedBrowseAdapter; import com.android.car.media.common.GridSpacingItemDecoration; import com.android.car.media.common.MediaItemMetadata; import com.android.car.media.common.browse.BrowsedMediaItems; @@ -48,7 +48,6 @@ import com.android.car.media.common.browse.MediaBrowserViewModel; import com.android.car.media.common.source.MediaSource; import com.android.car.media.widgets.AppBarController; import com.android.car.ui.FocusArea; -import com.android.car.ui.recyclerview.DelegatingContentLimitingAdapter; import com.android.car.ui.baselayout.Insets; import com.android.car.ui.toolbar.Toolbar; import com.android.car.uxr.LifeCycleObserverUxrContentLimiter; @@ -58,8 +57,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Stack; -import java.util.function.Predicate; -import java.util.stream.Collectors; /** * A view controller that implements the content forward browsing experience. @@ -81,9 +78,7 @@ public class BrowseViewController extends ViewControllerBase { private final RecyclerView mBrowseList; private final ImageView mErrorIcon; private final TextView mMessage; - private final DelegatingContentLimitingAdapter mLimitedBrowseAdapter; - private final BrowseAdapter mBrowseAdapter; - private int mMaxSpanSize = 1; + private final LimitedBrowseAdapter mLimitedBrowseAdapter; private String mSearchQuery; private final int mFadeDuration; private final int mLoadingIndicatorDelay; @@ -240,7 +235,7 @@ public class BrowseViewController extends ViewControllerBase { updateTabs((mediaSource != null) ? null : new ArrayList<>()); } - mBrowseAdapter.submitItems(null, null); + mLimitedBrowseAdapter.submitItems(null, null); stopLoadingIndicator(); ViewUtils.hideViewAnimated(mErrorIcon, mFadeDuration); ViewUtils.hideViewAnimated(mMessage, mFadeDuration); @@ -294,16 +289,10 @@ public class BrowseViewController extends ViewControllerBase { mBrowseList.addItemDecoration(new GridSpacingItemDecoration( activity.getResources().getDimensionPixelSize(R.dimen.grid_item_spacing))); - mBrowseAdapter = new BrowseAdapter(mBrowseList.getContext()); - mBrowseAdapter.registerObserver(mBrowseAdapterObserver); - mLimitedBrowseAdapter = new DelegatingContentLimitingAdapter(mBrowseAdapter, - R.id.browse_list_uxr_config); - mBrowseList.setAdapter(mLimitedBrowseAdapter); - GridLayoutManager manager = (GridLayoutManager) mBrowseList.getLayoutManager(); - mMaxSpanSize = manager.getSpanCount(); - manager.setSpanSizeLookup(mSpanSizeLookup); - + mLimitedBrowseAdapter = new LimitedBrowseAdapter( + new BrowseAdapter(mBrowseList.getContext()), manager, mBrowseAdapterObserver); + mBrowseList.setAdapter(mLimitedBrowseAdapter); mUxrContentLimiter = new LifeCycleObserverUxrContentLimiter( new UxrContentLimiterImpl(activity, R.xml.uxr_config)); @@ -311,9 +300,9 @@ public class BrowseViewController extends ViewControllerBase { activity.getLifecycle().addObserver(mUxrContentLimiter); mMediaBrowserViewModel.rootBrowsableHint().observe(activity, - mBrowseAdapter::setRootBrowsableViewType); + hint -> mLimitedBrowseAdapter.getBrowseAdapter().setRootBrowsableViewType(hint)); mMediaBrowserViewModel.rootPlayableHint().observe(activity, - mBrowseAdapter::setRootPlayableViewType); + hint -> mLimitedBrowseAdapter.getBrowseAdapter().setRootPlayableViewType(hint)); LiveData<FutureData<List<MediaItemMetadata>>> mediaItems = ifThenElse(mShowSearchResults, mMediaBrowserViewModel.getSearchedMediaItems(), mMediaBrowserViewModel.getBrowsedMediaItems()); @@ -359,19 +348,6 @@ public class BrowseViewController extends ViewControllerBase { } }; - private final GridLayoutManager.SpanSizeLookup mSpanSizeLookup = - new GridLayoutManager.SpanSizeLookup() { - @Override - public int getSpanSize(int position) { - if (mLimitedBrowseAdapter.getItemViewType(position) == - mLimitedBrowseAdapter.getScrollingLimitedMessageViewType()) { - return mMaxSpanSize; - } - - return mBrowseAdapter.getSpanSize(position, mMaxSpanSize); - } - }; - boolean onBackPressed() { boolean success = navigateBack(); if (!success && (mIsSearchController)) { @@ -579,7 +555,7 @@ public class BrowseViewController extends ViewControllerBase { ViewUtils.hideViewAnimated(mMessage, 0); // TODO(b/139759881) build a jank-free animation of the transition. mBrowseList.setAlpha(0f); - mBrowseAdapter.submitItems(null, null); + mLimitedBrowseAdapter.submitItems(null, null); if (forRoot) { if (Log.isLoggable(TAG, Log.INFO)) { @@ -605,7 +581,7 @@ public class BrowseViewController extends ViewControllerBase { mCallbacks.onRootLoaded(); updateTabs(items != null ? items : new ArrayList<>()); } else { - mBrowseAdapter.submitItems(getCurrentMediaItem(), items); + mLimitedBrowseAdapter.submitItems(getCurrentMediaItem(), items); } int duration = forRoot ? 0 : mFadeDuration; diff --git a/src/com/android/car/media/browse/BrowseAdapter.java b/src/com/android/car/media/browse/BrowseAdapter.java index 1d8ccb6..abf770d 100644 --- a/src/com/android/car/media/browse/BrowseAdapter.java +++ b/src/com/android/car/media/browse/BrowseAdapter.java @@ -51,6 +51,17 @@ import java.util.function.Consumer; */ public class BrowseAdapter extends ListAdapter<BrowseViewData, BrowseViewHolder> { private static final String TAG = "BrowseAdapter"; + + /** + * Listens to the list data changes. + */ + public interface OnListChangedListener { + /** + * Called when {@link #onCurrentListChanged(List, List)} is called. + */ + void onListChanged(List<BrowseViewData> previousList, List<BrowseViewData> currentList); + } + @NonNull private final Context mContext; @NonNull @@ -63,6 +74,8 @@ public class BrowseAdapter extends ListAdapter<BrowseViewData, BrowseViewHolder> private BrowseItemViewType mRootBrowsableViewType = BrowseItemViewType.LIST_ITEM; private BrowseItemViewType mRootPlayableViewType = BrowseItemViewType.LIST_ITEM; + private OnListChangedListener mOnListChangedListener; + private static final DiffUtil.ItemCallback<BrowseViewData> DIFF_CALLBACK = new DiffUtil.ItemCallback<BrowseViewData>() { @Override @@ -145,6 +158,13 @@ public class BrowseAdapter extends ListAdapter<BrowseViewData, BrowseViewHolder> return viewType.getSpanSize(maxSpanSize); } + /** + * Sets a listener to listen for the list data changes. + */ + public void setOnListChangedListener(OnListChangedListener onListChangedListener) { + mOnListChangedListener = onListChangedListener; + } + @NonNull @Override public BrowseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -176,6 +196,15 @@ public class BrowseAdapter extends ListAdapter<BrowseViewData, BrowseViewHolder> return getItem(position).mViewType.ordinal(); } + @Override + public void onCurrentListChanged(@NonNull List<BrowseViewData> previousList, + @NonNull List<BrowseViewData> currentList) { + super.onCurrentListChanged(previousList, currentList); + if (mOnListChangedListener != null) { + mOnListChangedListener.onListChanged(previousList, currentList); + } + } + public void submitItems(@Nullable MediaItemMetadata parentItem, @Nullable List<MediaItemMetadata> children) { mParentMediaItem = parentItem; diff --git a/src/com/android/car/media/browse/BrowseViewHolder.java b/src/com/android/car/media/browse/BrowseViewHolder.java index aa4449f..d30166e 100644 --- a/src/com/android/car/media/browse/BrowseViewHolder.java +++ b/src/com/android/car/media/browse/BrowseViewHolder.java @@ -34,7 +34,7 @@ import com.android.car.media.common.MediaItemMetadata; /** * Generic {@link RecyclerView.ViewHolder} to use for all views in the {@link BrowseAdapter} */ -class BrowseViewHolder extends RecyclerView.ViewHolder { +public class BrowseViewHolder extends RecyclerView.ViewHolder { private final TextView mTitle; private final TextView mSubtitle; private final ImageView mAlbumArt; diff --git a/src/com/android/car/media/browse/LimitedBrowseAdapter.java b/src/com/android/car/media/browse/LimitedBrowseAdapter.java new file mode 100644 index 0000000..bbca980 --- /dev/null +++ b/src/com/android/car/media/browse/LimitedBrowseAdapter.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.media.browse; + +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.car.media.R; +import com.android.car.media.common.MediaItemMetadata; +import com.android.car.ui.recyclerview.DelegatingContentLimitingAdapter; + +import java.util.List; + +/** + * Provides list limiting functionality to {@link BrowseAdapter}. + */ +public class LimitedBrowseAdapter extends DelegatingContentLimitingAdapter<BrowseViewHolder> { + + private final BrowseAdapter mBrowseAdapter; + private final GridLayoutManager mLayoutManager; + private final int mMaxSpanSize; + + @Nullable private String mAnchorId; + + public LimitedBrowseAdapter(BrowseAdapter browseAdapter, GridLayoutManager manager, + BrowseAdapter.Observer browseAdapterObserver) { + super(browseAdapter, R.id.browse_list_uxr_config); + + mBrowseAdapter = browseAdapter; + mLayoutManager = manager; + mMaxSpanSize = manager.getSpanCount(); + + mLayoutManager.setSpanSizeLookup(mSpanSizeLookup); + mBrowseAdapter.registerObserver(browseAdapterObserver); + mBrowseAdapter.setOnListChangedListener(((previousList, currentList) -> { + updateUnderlyingDataChanged(currentList.size(), validateAnchor()); + })); + } + + private final GridLayoutManager.SpanSizeLookup mSpanSizeLookup = + new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (getItemViewType(position) == getScrollingLimitedMessageViewType()) { + return mMaxSpanSize; + } + + int itemIndex = positionToIndex(position); + return mBrowseAdapter.getSpanSize(itemIndex, mMaxSpanSize); + } + }; + + public BrowseAdapter getBrowseAdapter() { + return mBrowseAdapter; + } + + /** + * @see BrowseAdapter#submitItems(MediaItemMetadata, List) + */ + public void submitItems(@Nullable MediaItemMetadata parentItem, + @Nullable List<MediaItemMetadata> items) { + mBrowseAdapter.submitItems(parentItem, items); + + if (items == null) { + updateUnderlyingDataChanged(0, 0); + return; + } + // We can't take any action with the new items as they must first go through the + // AsyncListDiffer of ListAdapter. This is handled in the OnListChangedListener. + } + + private int validateAnchor() { + if (mAnchorId == null) { + return 0; + } + + List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); + for (int i = 0; i < items.size(); i++) { + MediaItemMetadata mediaItem = items.get(i).mMediaItem; + if (mediaItem != null && mAnchorId.equals(mediaItem.getId())) { + return i; + } + } + + // The anchor isn't present in the new list, reset it. + mAnchorId = null; + return 0; + } + + + @Override + public int computeAnchorIndexWhenRestricting() { + List<BrowseViewData> items = mBrowseAdapter.getCurrentList(); + if (items.size() <= 0) { + mAnchorId = null; + return 0; + } + + int anchorIndex = (getFirstVisibleItemPosition() + getLastVisibleItemPosition()) / 2; + if (0 <= anchorIndex && anchorIndex < items.size()) { + MediaItemMetadata mediaItem = items.get(anchorIndex).mMediaItem; + mAnchorId = mediaItem != null ? mediaItem.getId() : null; + return anchorIndex; + } else { + mAnchorId = null; + return 0; + } + } + + private int getFirstVisibleItemPosition() { + int firstItem = mLayoutManager.findFirstCompletelyVisibleItemPosition(); + if (firstItem == RecyclerView.NO_POSITION) { + firstItem = mLayoutManager.findFirstVisibleItemPosition(); + } + return firstItem; + } + + private int getLastVisibleItemPosition() { + int lastItem = mLayoutManager.findLastCompletelyVisibleItemPosition(); + if (lastItem == RecyclerView.NO_POSITION) { + lastItem = mLayoutManager.findLastVisibleItemPosition(); + } + return lastItem; + } +} |