149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes/*
249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Copyright (C) 2014 The Android Open Source Project
349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes *
449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * you may not use this file except in compliance with the License.
649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * You may obtain a copy of the License at
749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes *
849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes *
1049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Unless required by applicable law or agreed to in writing, software
1149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
1249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * See the License for the specific language governing permissions and
1449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * limitations under the License.
1549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */
1649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.widget;
1849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
2149c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.content.Context;
2249c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.graphics.Canvas;
2349c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.graphics.Rect;
2449c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.graphics.drawable.Drawable;
25c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
2649c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
279840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.v7.graphics.drawable.DrawableWrapper;
2849c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.util.AttributeSet;
2918864e53490e04739f1063a7ee113549874f87b0Chris Banesimport android.view.MotionEvent;
3049c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.View;
3149c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.ViewGroup;
3249c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.widget.AbsListView;
3349c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.widget.ListAdapter;
3449c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.widget.ListView;
3549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
3649c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport java.lang.reflect.Field;
3749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
3849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes/**
3949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * This class contains a number of useful things for ListView. Mainly used by
4049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * {@link android.support.v7.widget.ListPopupWindow}.
4149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes *
4249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @hide
4349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */
448e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP)
4549c78900da0d43140fb602431fb93212bd7f6c70Chris Banespublic class ListViewCompat extends ListView {
4649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
4749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public static final int INVALID_POSITION = -1;
4849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public static final int NO_POSITION = -1;
4949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
5049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private static final int[] STATE_SET_NOTHING = new int[] { 0 };
5149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
5249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    final Rect mSelectorRect = new Rect();
5349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    int mSelectionLeftPadding = 0;
5449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    int mSelectionTopPadding = 0;
5549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    int mSelectionRightPadding = 0;
5649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    int mSelectionBottomPadding = 0;
5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
5818864e53490e04739f1063a7ee113549874f87b0Chris Banes    protected int mMotionPosition;
5918864e53490e04739f1063a7ee113549874f87b0Chris Banes
6049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    private Field mIsChildViewEnabled;
6149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
6280e66c1609421dacff3e49770411e201039aa1d8Chris Banes    private GateKeeperDrawable mSelector;
6380e66c1609421dacff3e49770411e201039aa1d8Chris Banes
6449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public ListViewCompat(Context context) {
6549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        this(context, null);
6649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
6749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
6849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public ListViewCompat(Context context, AttributeSet attrs) {
6949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        this(context, attrs, 0);
7049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
7149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
7249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public ListViewCompat(Context context, AttributeSet attrs, int defStyleAttr) {
7349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        super(context, attrs, defStyleAttr);
7449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
7549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        try {
7649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            mIsChildViewEnabled = AbsListView.class.getDeclaredField("mIsChildViewEnabled");
7749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            mIsChildViewEnabled.setAccessible(true);
7849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        } catch (NoSuchFieldException e) {
7949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            e.printStackTrace();
8049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
8149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
8249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
8349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    @Override
8449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public void setSelector(Drawable sel) {
85065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        mSelector = sel != null ? new GateKeeperDrawable(sel) : null;
8680e66c1609421dacff3e49770411e201039aa1d8Chris Banes        super.setSelector(mSelector);
8749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
88065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        final Rect padding = new Rect();
89065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        if (sel != null) {
90065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes            sel.getPadding(padding);
91065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        }
92065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes
9349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mSelectionLeftPadding = padding.left;
9449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mSelectionTopPadding = padding.top;
9549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mSelectionRightPadding = padding.right;
9649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        mSelectionBottomPadding = padding.bottom;
9749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
9849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
9949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    @Override
10049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void drawableStateChanged() {
10149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        super.drawableStateChanged();
102065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes
103065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        setSelectorEnabled(true);
10449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        updateSelectorStateCompat();
10549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
10649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
10749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    @Override
10849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void dispatchDraw(Canvas canvas) {
10949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final boolean drawSelectorOnTop = false;
11049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (!drawSelectorOnTop) {
11149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            drawSelectorCompat(canvas);
11249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
11349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
11449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        super.dispatchDraw(canvas);
11549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
11649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
11718864e53490e04739f1063a7ee113549874f87b0Chris Banes    @Override
11818864e53490e04739f1063a7ee113549874f87b0Chris Banes    public boolean onTouchEvent(MotionEvent ev) {
11918864e53490e04739f1063a7ee113549874f87b0Chris Banes        switch (ev.getAction()) {
12018864e53490e04739f1063a7ee113549874f87b0Chris Banes            case MotionEvent.ACTION_DOWN:
12118864e53490e04739f1063a7ee113549874f87b0Chris Banes                mMotionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
12218864e53490e04739f1063a7ee113549874f87b0Chris Banes                break;
12318864e53490e04739f1063a7ee113549874f87b0Chris Banes        }
12418864e53490e04739f1063a7ee113549874f87b0Chris Banes        return super.onTouchEvent(ev);
12518864e53490e04739f1063a7ee113549874f87b0Chris Banes    }
12618864e53490e04739f1063a7ee113549874f87b0Chris Banes
12749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void updateSelectorStateCompat() {
12849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        Drawable selector = getSelector();
1293228edfc4f8385dad5ae502f4a1d1343b9ec728fChris Banes        if (selector != null && shouldShowSelectorCompat()) {
1303228edfc4f8385dad5ae502f4a1d1343b9ec728fChris Banes            selector.setState(getDrawableState());
13149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
13249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
13349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
13449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected boolean shouldShowSelectorCompat() {
1353228edfc4f8385dad5ae502f4a1d1343b9ec728fChris Banes        return touchModeDrawsInPressedStateCompat() && isPressed();
13649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
13749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
13849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected boolean touchModeDrawsInPressedStateCompat() {
13949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        return false;
14049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
14149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
14249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void drawSelectorCompat(Canvas canvas) {
14349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (!mSelectorRect.isEmpty()) {
14449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            final Drawable selector = getSelector();
145065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes            if (selector != null) {
146065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes                selector.setBounds(mSelectorRect);
147065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes                selector.draw(canvas);
148065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes            }
14949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
15049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
15149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
15249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    /**
15349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * Find a position that can be selected (i.e., is not a separator).
15449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *
15549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param position The starting position to look at.
15649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param lookDown Whether to look down for other positions.
15749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @return The next selectable position starting at position and then searching either up or
15849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *         down. Returns {@link #INVALID_POSITION} if nothing can be found.
15949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     */
16049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public int lookForSelectablePosition(int position, boolean lookDown) {
16149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final ListAdapter adapter = getAdapter();
16249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (adapter == null || isInTouchMode()) {
16349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            return INVALID_POSITION;
16449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
16549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
16649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int count = adapter.getCount();
16749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (!getAdapter().areAllItemsEnabled()) {
16849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (lookDown) {
16949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                position = Math.max(0, position);
17049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                while (position < count && !adapter.isEnabled(position)) {
17149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                    position++;
17249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                }
17349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            } else {
17449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                position = Math.min(position, count - 1);
17549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                while (position >= 0 && !adapter.isEnabled(position)) {
17649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                    position--;
17749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                }
17849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
17949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
18049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (position < 0 || position >= count) {
18149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                return INVALID_POSITION;
18249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
18349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            return position;
18449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        } else {
18549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (position < 0 || position >= count) {
18649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                return INVALID_POSITION;
18749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
18849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            return position;
18949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
19049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
19149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
19249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void positionSelectorLikeTouchCompat(int position, View sel, float x, float y) {
19349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        positionSelectorLikeFocusCompat(position, sel);
19449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
19549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        Drawable selector = getSelector();
19649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (selector != null && position != INVALID_POSITION) {
19749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            DrawableCompat.setHotspot(selector, x, y);
19849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
19949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
20049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
20149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void positionSelectorLikeFocusCompat(int position, View sel) {
20249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // If we're changing position, update the visibility since the selector
20349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // is technically being detached from the previous selection.
20449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final Drawable selector = getSelector();
20549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final boolean manageState = selector != null && position != INVALID_POSITION;
20649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (manageState) {
20749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            selector.setVisible(false, false);
20849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
20949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
21049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        positionSelectorCompat(position, sel);
21149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
21249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (manageState) {
21349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            final Rect bounds = mSelectorRect;
21449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            final float x = bounds.exactCenterX();
21549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            final float y = bounds.exactCenterY();
21649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            selector.setVisible(getVisibility() == VISIBLE, false);
21749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            DrawableCompat.setHotspot(selector, x, y);
21849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
21949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
22049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
22149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    protected void positionSelectorCompat(int position, View sel) {
22249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final Rect selectorRect = mSelectorRect;
22349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
22449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
22549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // Adjust for selection padding.
22649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        selectorRect.left -= mSelectionLeftPadding;
22749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        selectorRect.top -= mSelectionTopPadding;
22849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        selectorRect.right += mSelectionRightPadding;
22949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        selectorRect.bottom += mSelectionBottomPadding;
23049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
23149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        try {
23249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            // AbsListView.mIsChildViewEnabled controls the selector's state so we need to
23344ece5ceaaee232f9139ccbde40ac4a5e642bdeeChristophe Beyls            // modify its value
23449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            final boolean isChildViewEnabled = mIsChildViewEnabled.getBoolean(this);
23549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (sel.isEnabled() != isChildViewEnabled) {
23649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                mIsChildViewEnabled.set(this, !isChildViewEnabled);
23749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                if (position != INVALID_POSITION) {
23849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                    refreshDrawableState();
23949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                }
24049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
24149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        } catch (IllegalAccessException e) {
24249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            e.printStackTrace();
24349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
24449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
24549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
24649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    /**
24749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * Measures the height of the given range of children (inclusive) and returns the height
24849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * with this ListView's padding and divider heights included. If maxHeight is provided, the
24949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * measuring will stop when the current height reaches maxHeight.
25049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *
25149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param widthMeasureSpec             The width measure spec to be given to a child's
25249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     {@link View#measure(int, int)}.
25349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param startPosition                The position of the first child to be shown.
25449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param endPosition                  The (inclusive) position of the last child to be
25549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     shown. Specify {@link #NO_POSITION} if the last child
25649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     should be the last available child from the adapter.
25749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param maxHeight                    The maximum height that will be returned (if all the
25849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     children don't fit in this value, this value will be
25949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     returned).
26049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @param disallowPartialChildPosition In general, whether the returned height should only
26149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     contain entire children. This is more powerful--it is
26249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     the first inclusive position at which partial
26349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     children will not be allowed. Example: it looks nice
26449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     to have at least 3 completely visible children, and
26549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     in portrait this will most likely fit; but in
26649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     landscape there could be times when even 2 children
26749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     can not be completely shown, so a value of 2
26849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     (remember, inclusive) would be good (assuming
26949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     *                                     startPosition is 0).
27049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     * @return The height of this ListView with the given children.
27149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes     */
27249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition,
27349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            int endPosition, final int maxHeight,
27449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            int disallowPartialChildPosition) {
27549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
27649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int paddingTop = getListPaddingTop();
27749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int paddingBottom = getListPaddingBottom();
27849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int paddingLeft = getListPaddingLeft();
27949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int paddingRight = getListPaddingRight();
28049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int reportedDividerHeight = getDividerHeight();
28149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final Drawable divider = getDivider();
28249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
28349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final ListAdapter adapter = getAdapter();
28449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
28549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        if (adapter == null) {
28649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            return paddingTop + paddingBottom;
28749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
28849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
28949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // Include the padding of the list
29049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        int returnedHeight = paddingTop + paddingBottom;
29149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        final int dividerHeight = ((reportedDividerHeight > 0) && divider != null)
29249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                ? reportedDividerHeight : 0;
29349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
29449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // The previous height value that was less than maxHeight and contained
29549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // no partial children
29649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        int prevHeightWithoutPartialChild = 0;
29749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
29849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        View child = null;
29949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        int viewType = 0;
30049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        int count = adapter.getCount();
30149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        for (int i = 0; i < count; i++) {
30249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            int newType = adapter.getItemViewType(i);
30349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (newType != viewType) {
30449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                child = null;
30549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                viewType = newType;
30649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
30749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            child = adapter.getView(i, child, this);
30849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
30949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            // Compute child height spec
31049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            int heightMeasureSpec;
31133be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            ViewGroup.LayoutParams childLp = child.getLayoutParams();
31233be76574d6b384d1e5be0946c8b19613cff0abbChris Banes
31333be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            if (childLp == null) {
31433be76574d6b384d1e5be0946c8b19613cff0abbChris Banes                childLp = generateDefaultLayoutParams();
31533be76574d6b384d1e5be0946c8b19613cff0abbChris Banes                child.setLayoutParams(childLp);
31633be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            }
31733be76574d6b384d1e5be0946c8b19613cff0abbChris Banes
31833be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            if (childLp.height > 0) {
31949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                heightMeasureSpec = MeasureSpec.makeMeasureSpec(childLp.height,
32049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        MeasureSpec.EXACTLY);
32149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            } else {
32249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
32349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
32449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            child.measure(widthMeasureSpec, heightMeasureSpec);
32549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
326c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            // Since this view was measured directly against the parent measure
32733be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            // spec, we must measure it again before reuse.
32833be76574d6b384d1e5be0946c8b19613cff0abbChris Banes            child.forceLayout();
32933be76574d6b384d1e5be0946c8b19613cff0abbChris Banes
33049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (i > 0) {
33149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                // Count the divider for all but one child
33249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                returnedHeight += dividerHeight;
33349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
33449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
33549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            returnedHeight += child.getMeasuredHeight();
33649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
33749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if (returnedHeight >= maxHeight) {
33849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                // We went over, figure out which height to return.  If returnedHeight >
33949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                // maxHeight, then the i'th position did not fit completely.
34049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
34149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        && (i > disallowPartialChildPosition) // We've past the min pos
34249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        && (prevHeightWithoutPartialChild > 0) // We have a prev height
34349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        && (returnedHeight != maxHeight) // i'th child did not fit completely
34449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        ? prevHeightWithoutPartialChild
34549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                        : maxHeight;
34649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
34749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
34849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
34949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes                prevHeightWithoutPartialChild = returnedHeight;
35049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes            }
35149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        }
35249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
35349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // At this point, we went through the range of children, and they each
35449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        // completely fit, so return the returnedHeight
35549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes        return returnedHeight;
35649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes    }
35749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes
35880e66c1609421dacff3e49770411e201039aa1d8Chris Banes    protected void setSelectorEnabled(boolean enabled) {
359065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        if (mSelector != null) {
360065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes            mSelector.setEnabled(enabled);
361065fe5b9b48c7edee12fb5fcbf776b29a9ebd51eChris Banes        }
36280e66c1609421dacff3e49770411e201039aa1d8Chris Banes    }
36380e66c1609421dacff3e49770411e201039aa1d8Chris Banes
36480e66c1609421dacff3e49770411e201039aa1d8Chris Banes    private static class GateKeeperDrawable extends DrawableWrapper {
36580e66c1609421dacff3e49770411e201039aa1d8Chris Banes        private boolean mEnabled;
36680e66c1609421dacff3e49770411e201039aa1d8Chris Banes
36780e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public GateKeeperDrawable(Drawable drawable) {
36880e66c1609421dacff3e49770411e201039aa1d8Chris Banes            super(drawable);
36980e66c1609421dacff3e49770411e201039aa1d8Chris Banes            mEnabled = true;
37080e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
37180e66c1609421dacff3e49770411e201039aa1d8Chris Banes
37280e66c1609421dacff3e49770411e201039aa1d8Chris Banes        void setEnabled(boolean enabled) {
37380e66c1609421dacff3e49770411e201039aa1d8Chris Banes            mEnabled = enabled;
37480e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
37580e66c1609421dacff3e49770411e201039aa1d8Chris Banes
37680e66c1609421dacff3e49770411e201039aa1d8Chris Banes        @Override
37780e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public boolean setState(int[] stateSet) {
37880e66c1609421dacff3e49770411e201039aa1d8Chris Banes            if (mEnabled) {
37980e66c1609421dacff3e49770411e201039aa1d8Chris Banes                return super.setState(stateSet);
38080e66c1609421dacff3e49770411e201039aa1d8Chris Banes            }
38180e66c1609421dacff3e49770411e201039aa1d8Chris Banes            return false;
38280e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
38380e66c1609421dacff3e49770411e201039aa1d8Chris Banes
38480e66c1609421dacff3e49770411e201039aa1d8Chris Banes        @Override
38580e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public void draw(Canvas canvas) {
38680e66c1609421dacff3e49770411e201039aa1d8Chris Banes            if (mEnabled) {
38780e66c1609421dacff3e49770411e201039aa1d8Chris Banes                super.draw(canvas);
38880e66c1609421dacff3e49770411e201039aa1d8Chris Banes            }
38980e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
39080e66c1609421dacff3e49770411e201039aa1d8Chris Banes
39180e66c1609421dacff3e49770411e201039aa1d8Chris Banes        @Override
39280e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public void setHotspot(float x, float y) {
39380e66c1609421dacff3e49770411e201039aa1d8Chris Banes            if (mEnabled) {
39480e66c1609421dacff3e49770411e201039aa1d8Chris Banes                super.setHotspot(x, y);
39580e66c1609421dacff3e49770411e201039aa1d8Chris Banes            }
39680e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
39780e66c1609421dacff3e49770411e201039aa1d8Chris Banes
39880e66c1609421dacff3e49770411e201039aa1d8Chris Banes        @Override
39980e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public void setHotspotBounds(int left, int top, int right, int bottom) {
40080e66c1609421dacff3e49770411e201039aa1d8Chris Banes            if (mEnabled) {
40180e66c1609421dacff3e49770411e201039aa1d8Chris Banes                super.setHotspotBounds(left, top, right, bottom);
40280e66c1609421dacff3e49770411e201039aa1d8Chris Banes            }
40380e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
40480e66c1609421dacff3e49770411e201039aa1d8Chris Banes
40580e66c1609421dacff3e49770411e201039aa1d8Chris Banes        @Override
40680e66c1609421dacff3e49770411e201039aa1d8Chris Banes        public boolean setVisible(boolean visible, boolean restart) {
40780e66c1609421dacff3e49770411e201039aa1d8Chris Banes            if (mEnabled) {
40880e66c1609421dacff3e49770411e201039aa1d8Chris Banes                return super.setVisible(visible, restart);
40980e66c1609421dacff3e49770411e201039aa1d8Chris Banes            }
41080e66c1609421dacff3e49770411e201039aa1d8Chris Banes            return false;
41180e66c1609421dacff3e49770411e201039aa1d8Chris Banes        }
41280e66c1609421dacff3e49770411e201039aa1d8Chris Banes    }
41349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes}
414