163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay/*
263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * Copyright 2017 The Android Open Source Project
363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay *
463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * Licensed under the Apache License, Version 2.0 (the "License");
563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * you may not use this file except in compliance with the License.
663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * You may obtain a copy of the License at
763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay *
863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay *      http://www.apache.org/licenses/LICENSE-2.0
963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay *
1063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * Unless required by applicable law or agreed to in writing, software
1163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * distributed under the License is distributed on an "AS IS" BASIS,
1263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * See the License for the specific language governing permissions and
1463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * limitations under the License.
1563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay */
1663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
172a32c7e1264b14a20ed900abadea828b804a46ceAurimas Liutikaspackage androidx.recyclerview.selection;
1863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.core.util.Preconditions.checkArgument;
2063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
2163d2846409d84487d4856d3b8d18cc4684352e29Steve McKayimport android.graphics.Canvas;
2263d2846409d84487d4856d3b8d18cc4684352e29Steve McKayimport android.graphics.Point;
2363d2846409d84487d4856d3b8d18cc4684352e29Steve McKayimport android.graphics.Rect;
2463d2846409d84487d4856d3b8d18cc4684352e29Steve McKayimport android.graphics.drawable.Drawable;
2560dadaeed4f5cee272b575dfde6c02e3506a2fa0Aurimas Liutikasimport android.view.View;
2660dadaeed4f5cee272b575dfde6c02e3506a2fa0Aurimas Liutikas
27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.DrawableRes;
28ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull;
2960dadaeed4f5cee272b575dfde6c02e3506a2fa0Aurimas Liutikasimport androidx.recyclerview.selection.SelectionTracker.SelectionPredicate;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.recyclerview.widget.GridLayoutManager;
31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.recyclerview.widget.RecyclerView;
32ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.recyclerview.widget.RecyclerView.ItemDecoration;
33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.recyclerview.widget.RecyclerView.OnScrollListener;
3463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
3563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay/**
3663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay * RecyclerView backed {@link BandSelectionHelper.BandHost}.
3763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay */
3863d2846409d84487d4856d3b8d18cc4684352e29Steve McKayfinal class DefaultBandHost<K> extends GridModel.GridHost<K> {
3963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
4063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    private static final Rect NILL_RECT = new Rect(0, 0, 0, 0);
4163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
427fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    private final RecyclerView mRecyclerView;
4363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    private final Drawable mBand;
4463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    private final ItemKeyProvider<K> mKeyProvider;
4563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    private final SelectionPredicate<K> mSelectionPredicate;
4663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
4763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    DefaultBandHost(
487fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay            @NonNull RecyclerView recyclerView,
4963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay            @DrawableRes int bandOverlayId,
507fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay            @NonNull ItemKeyProvider<K> keyProvider,
517fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay            @NonNull SelectionPredicate<K> selectionPredicate) {
5263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
537fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        checkArgument(recyclerView != null);
5463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
557fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView = recyclerView;
567fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mBand = mRecyclerView.getContext().getResources().getDrawable(bandOverlayId);
5763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
5863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        checkArgument(mBand != null);
5963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        checkArgument(keyProvider != null);
6063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        checkArgument(selectionPredicate != null);
6163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
6263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        mKeyProvider = keyProvider;
6363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        mSelectionPredicate = selectionPredicate;
6463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
657fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView.addItemDecoration(
6663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                new ItemDecoration() {
6763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                    @Override
6863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                    public void onDrawOver(
6963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                            Canvas canvas,
7063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                            RecyclerView unusedParent,
7163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                            RecyclerView.State unusedState) {
7263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                        DefaultBandHost.this.onDrawBand(canvas);
7363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                    }
7463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay                });
7563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
7663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
7763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
7863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    GridModel<K> createGridModel() {
7963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        return new GridModel<>(this, mKeyProvider, mSelectionPredicate);
8063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
8163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
8263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
8363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    int getAdapterPositionAt(int index) {
847fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        return mRecyclerView.getChildAdapterPosition(mRecyclerView.getChildAt(index));
8563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
8663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
8763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
887fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    void addOnScrollListener(@NonNull OnScrollListener listener) {
897fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView.addOnScrollListener(listener);
9063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
9163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
9263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
937fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    void removeOnScrollListener(@NonNull OnScrollListener listener) {
947fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView.removeOnScrollListener(listener);
9563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
9663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
9763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
987fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    Point createAbsolutePoint(@NonNull Point relativePoint) {
997fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(),
1007fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay                relativePoint.y + mRecyclerView.computeVerticalScrollOffset());
10163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
10263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
10363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
10463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    Rect getAbsoluteRectForChildViewAt(int index) {
1057fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        final View child = mRecyclerView.getChildAt(index);
10663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        final Rect childRect = new Rect();
10763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        child.getHitRect(childRect);
1087fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        childRect.left += mRecyclerView.computeHorizontalScrollOffset();
1097fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        childRect.right += mRecyclerView.computeHorizontalScrollOffset();
1107fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        childRect.top += mRecyclerView.computeVerticalScrollOffset();
1117fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        childRect.bottom += mRecyclerView.computeVerticalScrollOffset();
11263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        return childRect;
11363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
11463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
11563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
11663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    int getVisibleChildCount() {
1177fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        return mRecyclerView.getChildCount();
11863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
11963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
12063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
12163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    int getColumnCount() {
1227fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
12363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        if (layoutManager instanceof GridLayoutManager) {
12463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay            return ((GridLayoutManager) layoutManager).getSpanCount();
12563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        }
12663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
12763d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        // Otherwise, it is a list with 1 column.
12863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        return 1;
12963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
13063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
13163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
1327fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    void showBand(@NonNull Rect rect) {
13363d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        mBand.setBounds(rect);
1347fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        // TODO: mRecyclerView.invalidateItemDecorations() should work, but it isn't currently.
13563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        // NOTE: That without invalidating rv, the band only gets updated
13663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        // when the pointer moves off a the item view into "NO_POSITION" territory.
1377fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView.invalidate();
13863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
13963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
14063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
14163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    void hideBand() {
14263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        mBand.setBounds(NILL_RECT);
1437fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        // TODO: mRecyclerView.invalidateItemDecorations() should work, but it isn't currently.
1447fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        mRecyclerView.invalidate();
14563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
14663d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
1477fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay    private void onDrawBand(@NonNull Canvas c) {
14863d2846409d84487d4856d3b8d18cc4684352e29Steve McKay        mBand.draw(c);
14963d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
15063d2846409d84487d4856d3b8d18cc4684352e29Steve McKay
15163d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    @Override
15263d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    boolean hasView(int pos) {
1537fb763509e07f98d650efc25c91bff8b1cb239acSteve McKay        return mRecyclerView.findViewHolderForAdapterPosition(pos) != null;
15463d2846409d84487d4856d3b8d18cc4684352e29Steve McKay    }
15563d2846409d84487d4856d3b8d18cc4684352e29Steve McKay}
156