1668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar/*
2668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * Copyright (C) 2014 The Android Open Source Project
3668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar *
4668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * you may not use this file except in compliance with the License.
6668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * You may obtain a copy of the License at
7668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar *
8668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar *
10668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * See the License for the specific language governing permissions and
14668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * limitations under the License.
15668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar */
16668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
17668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarpackage android.support.v7.widget;
18668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
19668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarimport android.util.Log;
20668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarimport android.view.View;
21668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarimport android.view.ViewGroup;
22668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
23668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarimport java.util.ArrayList;
24668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarimport java.util.List;
25668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
26668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar/**
27668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * Helper class to manage children.
28668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * <p>
29668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
30668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
31668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * like getChildAt, getChildCount etc. These methods ignore hidden children.
32668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * <p>
33668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * When RecyclerView needs direct access to the view group children, it can call unfiltered
34668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar * methods like get getUnfilteredChildCount or getUnfilteredChildAt.
35668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar */
36668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyarclass ChildHelper {
37668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
38668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    private static final boolean DEBUG = false;
39668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
40668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    private static final String TAG = "ChildrenHelper";
41668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
42668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    final Callback mCallback;
43668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
44668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    final Bucket mBucket;
45668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
46668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    final List<View> mHiddenViews;
47668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
48668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    ChildHelper(Callback callback) {
49668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mCallback = callback;
50668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket = new Bucket();
51668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mHiddenViews = new ArrayList<View>();
52668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
53668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
54668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
55344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     * Marks a child view as hidden
56344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     *
57344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     * @param child  View to hide.
58344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     */
59344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    private void hideViewInternal(View child) {
60344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mHiddenViews.add(child);
61344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        mCallback.onEnteredHiddenState(child);
62344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
63344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
64344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    /**
65344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     * Unmarks a child view as hidden.
66344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     *
67344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     * @param child  View to hide.
68344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev     */
69344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    private boolean unhideViewInternal(View child) {
70344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        if (mHiddenViews.remove(child)) {
71344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            mCallback.onLeftHiddenState(child);
72344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            return true;
73344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        } else {
74344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            return false;
75344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        }
76344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    }
77344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
78344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev    /**
79668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Adds a view to the ViewGroup
80668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
81668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param child  View to add.
82668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param hidden If set to true, this item will be invisible from regular methods.
83668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
84668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void addView(View child, boolean hidden) {
85668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        addView(child, -1, hidden);
86668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
87668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
88668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
89668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Add a view to the ViewGroup at an index
90668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
91668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param child  View to add.
92668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index  Index of the child from the regular perspective (excluding hidden views).
93668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *               ChildHelper offsets this index to actual ViewGroup index.
94668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param hidden If set to true, this item will be invisible from regular methods.
95668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
96668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void addView(View child, int index, boolean hidden) {
97668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset;
98668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (index < 0) {
99668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            offset = mCallback.getChildCount();
100668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        } else {
101668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            offset = getOffset(index);
102668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
103668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket.insert(offset, hidden);
104668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (hidden) {
105344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            hideViewInternal(child);
106668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
107de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.addView(child, offset);
108668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
109668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
110668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
111668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
112668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
113668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    private int getOffset(int index) {
1149bdc605f26da69e3aff157cf3746b767d77988f0Yigit Boyar        if (index < 0) {
1159bdc605f26da69e3aff157cf3746b767d77988f0Yigit Boyar            return -1; //anything below 0 won't work as diff will be undefined.
1169bdc605f26da69e3aff157cf3746b767d77988f0Yigit Boyar        }
117668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int limit = mCallback.getChildCount();
118668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        int offset = index;
119668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        while (offset < limit) {
120668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            final int removedBefore = mBucket.countOnesBefore(offset);
121668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            final int diff = index - (offset - removedBefore);
122668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (diff == 0) {
123668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                while (mBucket.get(offset)) { // ensure this offset is not hidden
124668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    offset ++;
125668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
126668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return offset;
127668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
128668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                offset += diff;
129668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
130668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
131668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return -1;
132668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
133668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
134668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
135668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Removes the provided View from underlying RecyclerView.
136668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
137668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param view The view to remove.
138668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
139668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void removeView(View view) {
140668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        int index = mCallback.indexOfChild(view);
141668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (index < 0) {
142668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return;
143668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
144668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (mBucket.remove(index)) {
145344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            unhideViewInternal(view);
146668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
147de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.removeViewAt(index);
148668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
149668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "remove View off:" + index + "," + this);
150668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
151668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
152668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
153668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
154668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Removes the view at the provided index from RecyclerView.
155668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
156668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index Index of the child from the regular perspective (excluding hidden views).
157668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *              ChildHelper offsets this index to actual ViewGroup index.
158668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
159668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void removeViewAt(int index) {
160668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset = getOffset(index);
161668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final View view = mCallback.getChildAt(offset);
162668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (view == null) {
163668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return;
164668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
165668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (mBucket.remove(offset)) {
166344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            unhideViewInternal(view);
167668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
168de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.removeViewAt(offset);
169668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
170668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
171668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
172668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
173668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
174668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
175668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Returns the child at provided index.
176668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
177668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index Index of the child to return in regular perspective.
178668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
179668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    View getChildAt(int index) {
180668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset = getOffset(index);
181668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return mCallback.getChildAt(offset);
182668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
183668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
184668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
185668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Removes all views from the ViewGroup including the hidden ones.
186668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
187668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void removeAllViewsUnfiltered() {
188668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket.reset();
189344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        for (int i = mHiddenViews.size() - 1; i >= 0; i--) {
190344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            mCallback.onLeftHiddenState(mHiddenViews.get(i));
191344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            mHiddenViews.remove(i);
192344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        }
193de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.removeAllViews();
194668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
195668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "removeAllViewsUnfiltered");
196668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
197668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
198668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
199668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
200668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * This can be used to find a disappearing view by position.
201668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
202668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param position The adapter position of the item.
203668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param type     View type, can be {@link RecyclerView#INVALID_TYPE}.
204668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return         A hidden view with a valid ViewHolder that matches the position and type.
205668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
206668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    View findHiddenNonRemovedView(int position, int type) {
207668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int count = mHiddenViews.size();
208668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        for (int i = 0; i < count; i++) {
209668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            final View view = mHiddenViews.get(i);
210668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
2115031b30272a0178845f88d06adddb204dd833779Yigit Boyar            if (holder.getLayoutPosition() == position && !holder.isInvalid() && !holder.isRemoved()
2125031b30272a0178845f88d06adddb204dd833779Yigit Boyar                    && (type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
213668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return view;
214668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
215668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
216668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return null;
217668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
218668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
219668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
220668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Attaches the provided view to the underlying ViewGroup.
221668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
222668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param child        Child to attach.
223668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index        Index of the child to attach in regular perspective.
224668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param layoutParams LayoutParams for the child.
225668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param hidden       If set to true, this item will be invisible to the regular methods.
226668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
227668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
228668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            boolean hidden) {
229668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset;
230668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (index < 0) {
231668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            offset = mCallback.getChildCount();
232668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        } else {
233668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            offset = getOffset(index);
234668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
235668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket.insert(offset, hidden);
23690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        if (hidden) {
237344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            hideViewInternal(child);
23890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        }
239de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.attachViewToParent(child, offset, layoutParams);
240668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
241668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," +
242668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    "h:" + hidden + ", " + this);
243668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
244668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
245668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
246668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
247668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Returns the number of children that are not hidden.
248668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
249668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return Number of children that are not hidden.
250668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @see #getChildAt(int)
251668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
252668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    int getChildCount() {
253668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return mCallback.getChildCount() - mHiddenViews.size();
254668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
255668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
256668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
257668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Returns the total number of children.
258668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
259668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return The total number of children including the hidden views.
260668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @see #getUnfilteredChildAt(int)
261668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
262668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    int getUnfilteredChildCount() {
263668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return mCallback.getChildCount();
264668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
265668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
266668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
267668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Returns a child by ViewGroup offset. ChildHelper won't offset this index.
268668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
269668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index ViewGroup index of the child to return.
270668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return The view in the provided index.
271668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
272668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    View getUnfilteredChildAt(int index) {
273668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return mCallback.getChildAt(index);
274668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
275668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
276668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
277668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Detaches the view at the provided index.
278668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
279668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param index Index of the child to return in regular perspective.
280668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
281668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void detachViewFromParent(int index) {
282668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset = getOffset(index);
283668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket.remove(offset);
284de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar        mCallback.detachViewFromParent(offset);
285668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
286668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
287668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
288668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
289668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
290668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
291668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Returns the index of the child in regular perspective.
292668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
293668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param child The child whose index will be returned.
294668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return The regular perspective index of the child or -1 if it does not exists.
295668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
296668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    int indexOfChild(View child) {
297668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int index = mCallback.indexOfChild(child);
298668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (index == -1) {
299668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return -1;
300668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
301668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (mBucket.get(index)) {
302668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (DEBUG) {
303668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                throw new IllegalArgumentException("cannot get index of a hidden child");
304668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
305668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return -1;
306668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
307668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
308668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        // reverse the index
309668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return index - mBucket.countOnesBefore(index);
310668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
311668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
312668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
313a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar     * Returns whether a View is visible to LayoutManager or not.
314a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar     *
315a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar     * @param view The child view to check. Should be a child of the Callback.
316a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar     * @return True if the View is not visible to LayoutManager
317a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar     */
318a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar    boolean isHidden(View view) {
319a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar        return mHiddenViews.contains(view);
320a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar    }
321a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar
322a5dc6f3eb86aacd2b287f6ea588aa1d900f6702aYigit Boyar    /**
323668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Marks a child view as hidden.
324668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
325668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param view The view to hide.
326668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
327668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    void hide(View view) {
328668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int offset = mCallback.indexOfChild(view);
329668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (offset < 0) {
330668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
331668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
33218ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar        if (DEBUG && mBucket.get(offset)) {
33318ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar            throw new RuntimeException("trying to hide same view twice, how come ? " + view);
33418ae336dbaae3db982f38cde08936c9f0243757bYigit Boyar        }
335668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        mBucket.set(offset);
336344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        hideViewInternal(view);
337668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (DEBUG) {
338668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this);
339668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
340668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
341668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
3429051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    /**
3439051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     * Moves a child view from hidden list to regular list.
3449051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     * Calling this method should probably be followed by a detach, otherwise, it will suddenly
3459051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     * show up in LayoutManager's children list.
3469051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     *
3479051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     * @param view The hidden View to unhide
3489051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar     */
3499051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    void unhide(View view) {
3509051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        final int offset = mCallback.indexOfChild(view);
3519051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        if (offset < 0) {
3529051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            throw new IllegalArgumentException("view is not a child, cannot hide " + view);
3539051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        }
3549051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        if (!mBucket.get(offset)) {
3559051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar            throw new RuntimeException("trying to unhide a view that was not hidden" + view);
3569051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        }
3579051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        mBucket.clear(offset);
3589051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar        unhideViewInternal(view);
3599051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar    }
3609051a368c00c40f8679ae67147b9f5af6f93638dYigit Boyar
361668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    @Override
362668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    public String toString() {
36390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        return mBucket.toString() + ", hidden list:" + mHiddenViews.size();
364668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
365668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
366668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
367668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Removes a view from the ViewGroup if it is hidden.
368668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     *
369668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @param view The view to remove.
370668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * @return True if the View is found and it is hidden. False otherwise.
371668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
372668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    boolean removeViewIfHidden(View view) {
373668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final int index = mCallback.indexOfChild(view);
374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        if (index == -1) {
375344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            if (unhideViewInternal(view) && DEBUG) {
376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                throw new IllegalStateException("view is in hidden list but not in view group");
377668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
378668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return true;
379668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
380668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        if (mBucket.get(index)) {
381668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            mBucket.remove(index);
382344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev            if (!unhideViewInternal(view) && DEBUG) {
383668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                throw new IllegalStateException(
384668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        "removed a hidden view but it is not in hidden views list");
385668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
386de5f54783555fa2e778c4ed6760472a002b2589bYigit Boyar            mCallback.removeViewAt(index);
387668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return true;
388668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
389668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        return false;
390668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
391668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
392668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    /**
393668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     * Bitset implementation that provides methods to offset indices.
394668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar     */
395668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    static class Bucket {
396668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
397668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final static int BITS_PER_WORD = Long.SIZE;
398668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
399668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        final static long LAST_BIT = 1L << (Long.SIZE - 1);
400668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
401668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        long mData = 0;
402668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
403668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        Bucket next;
404668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
405668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void set(int index) {
406668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index >= BITS_PER_WORD) {
407668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ensureNext();
408668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                next.set(index - BITS_PER_WORD);
409668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
410668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mData |= 1L << index;
411668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
412668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
413668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
414668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private void ensureNext() {
415668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (next == null) {
416668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                next = new Bucket();
417668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
418668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
419668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
420668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void clear(int index) {
421668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index >= BITS_PER_WORD) {
422668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (next != null) {
423668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    next.clear(index - BITS_PER_WORD);
424668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
425668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
426668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mData &= ~(1L << index);
427668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
428668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
429668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
430668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
431668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        boolean get(int index) {
432668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index >= BITS_PER_WORD) {
433668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ensureNext();
434668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return next.get(index - BITS_PER_WORD);
435668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
436668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return (mData & (1L << index)) != 0;
437668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
438668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
439668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
440668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void reset() {
441668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            mData = 0;
442668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (next != null) {
443668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                next.reset();
444668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
445668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
446668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
447668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void insert(int index, boolean value) {
448668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index >= BITS_PER_WORD) {
449668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ensureNext();
450668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                next.insert(index - BITS_PER_WORD, value);
451668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
452668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final boolean lastBit = (mData & LAST_BIT) != 0;
453668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                long mask = (1L << index) - 1;
454668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final long before = mData & mask;
455668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final long after = ((mData & ~mask)) << 1;
456668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mData = before | after;
457668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (value) {
458668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    set(index);
459668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                } else {
460668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    clear(index);
461668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
462668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (lastBit || next != null) {
463668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    ensureNext();
464668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    next.insert(0, lastBit);
465668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
466668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
467668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
468668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
469668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        boolean remove(int index) {
470668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index >= BITS_PER_WORD) {
471668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ensureNext();
472668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return next.remove(index - BITS_PER_WORD);
473668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
474668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                long mask = (1L << index);
475668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final boolean value = (mData & mask) != 0;
476668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mData &= ~mask;
477668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mask = mask - 1;
478668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final long before = mData & mask;
479668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                // cannot use >> because it adds one.
480668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                final long after = Long.rotateRight(mData & ~mask, 1);
481668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                mData = before | after;
482668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (next != null) {
483668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    if (next.get(0)) {
484668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        set(BITS_PER_WORD - 1);
485668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    }
486668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    next.remove(0);
487668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
488668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return value;
489668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
490668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
491668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
492668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        int countOnesBefore(int index) {
493668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (next == null) {
494668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (index >= BITS_PER_WORD) {
495668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    return Long.bitCount(mData);
496668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
497668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return Long.bitCount(mData & ((1L << index) - 1));
498668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
499668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (index < BITS_PER_WORD) {
500668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return Long.bitCount(mData & ((1L << index) - 1));
501668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            } else {
502668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
503668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
504668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
505668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
506668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        @Override
507668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        public String toString() {
508668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            return next == null ? Long.toBinaryString(mData)
509668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    : next.toString() + "xx" + Long.toBinaryString(mData);
510668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
511668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
512668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
513668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    static interface Callback {
514668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
515668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        int getChildCount();
516668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
517668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void addView(View child, int index);
518668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
519668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        int indexOfChild(View view);
520668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
521668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void removeViewAt(int index);
522668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
523668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        View getChildAt(int offset);
524668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
525668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void removeAllViews();
526668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
527668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        RecyclerView.ViewHolder getChildViewHolder(View view);
528668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
529668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
530668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
531668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        void detachViewFromParent(int offset);
532344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
533344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        void onEnteredHiddenState(View child);
534344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev
535344e49fcc92aba485da10a983fe916e2e4750a77Vadim Tryshev        void onLeftHiddenState(View child);
536668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    }
537668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar}
538