162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu/*
262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * Copyright (C) 2013 The Android Open Source Project
362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu *
462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * Licensed under the Apache License, Version 2.0 (the "License");
562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * you may not use this file except in compliance with the License.
662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * You may obtain a copy of the License at
762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu *
862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu *      http://www.apache.org/licenses/LICENSE-2.0
962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu *
1062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * Unless required by applicable law or agreed to in writing, software
1162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * distributed under the License is distributed on an "AS IS" BASIS,
1262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * See the License for the specific language governing permissions and
1462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * limitations under the License.
1562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu */
1662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
1762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescupackage com.android.photos.views;
1862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
1962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.content.Context;
2062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.database.DataSetObservable;
2162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.database.DataSetObserver;
2262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.util.AttributeSet;
2362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.view.View;
2462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.view.ViewGroup;
2562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.AdapterView;
2662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.Filter;
2762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.Filterable;
2862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.FrameLayout;
2962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.GridView;
3062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.ListAdapter;
3162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport android.widget.WrapperListAdapter;
3262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
3362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescuimport java.util.ArrayList;
3462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
3562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu/**
3662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * A {@link GridView} that supports adding header rows in a
3762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * very similar way to {@link ListView}.
3862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu * See {@link HeaderGridView#addHeaderView(View, Object, boolean)}
3962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu */
4062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescupublic class HeaderGridView extends GridView {
4162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private static final String TAG = "HeaderGridView";
4262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
4362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    /**
4462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * A class that represents a fixed view in a list, for example a header at the top
4562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * or a footer at the bottom.
4662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     */
4762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private static class FixedViewInfo {
4862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        /** The view to add to the grid */
4962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public View view;
5062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public ViewGroup viewContainer;
5162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
5262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public Object data;
5362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        /** <code>true</code> if the fixed view should be selectable in the grid */
5462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean isSelectable;
5562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
5662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
5762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
5862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
5962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private void initHeaderGridView() {
6062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        super.setClipChildren(false);
6162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
6262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
6362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public HeaderGridView(Context context) {
6462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        super(context);
6562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        initHeaderGridView();
6662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
6762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
6862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public HeaderGridView(Context context, AttributeSet attrs) {
6962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        super(context, attrs);
7062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        initHeaderGridView();
7162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
7262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
7362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public HeaderGridView(Context context, AttributeSet attrs, int defStyle) {
7462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        super(context, attrs, defStyle);
7562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        initHeaderGridView();
7662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
7762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
7862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    @Override
7962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
8062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
8162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        ListAdapter adapter = getAdapter();
8262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
8362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumns());
8462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
8562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
8662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
8762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    @Override
8862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public void setClipChildren(boolean clipChildren) {
8962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu       // Ignore, since the header rows depend on not being clipped
9062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
9162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
9262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    /**
9362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * Add a fixed view to appear at the top of the grid. If addHeaderView is
9462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * called more than once, the views will appear in the order they were
9562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * added. Views added using this call can take focus if they want.
9662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * <p>
9762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
9862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * the supplied cursor with one that will also account for header views.
9962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     *
10062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @param v The view to add.
10162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @param data Data to associate with this view
10262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @param isSelectable whether the item is selectable
10362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     */
10462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public void addHeaderView(View v, Object data, boolean isSelectable) {
10562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        ListAdapter adapter = getAdapter();
10662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
10762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        if (adapter != null && ! (adapter instanceof HeaderViewGridAdapter)) {
10862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            throw new IllegalStateException(
10962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    "Cannot add header view to grid -- setAdapter has already been called.");
11062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
11162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
11262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        FixedViewInfo info = new FixedViewInfo();
11362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        FrameLayout fl = new FullWidthFixedViewLayout(getContext());
11462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        fl.addView(v);
11562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        info.view = v;
11662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        info.viewContainer = fl;
11762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        info.data = data;
11862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        info.isSelectable = isSelectable;
11962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        mHeaderViewInfos.add(info);
12062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
12162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        // in the case of re-adding a header view, or adding one later on,
12262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        // we need to notify the observer
12362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        if (adapter != null) {
12462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();
12562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
12662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
12762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
12862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    /**
12962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * Add a fixed view to appear at the top of the grid. If addHeaderView is
13062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * called more than once, the views will appear in the order they were
13162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * added. Views added using this call can take focus if they want.
13262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * <p>
13362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
13462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * the supplied cursor with one that will also account for header views.
13562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     *
13662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @param v The view to add.
13762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     */
13862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public void addHeaderView(View v) {
13962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        addHeaderView(v, null, true);
14062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
14162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
14262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public int getHeaderViewCount() {
14362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        return mHeaderViewInfos.size();
14462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
14562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
14662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    /**
14762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * Removes a previously-added header view.
14862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     *
14962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @param v The view to remove
15062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * @return true if the view was removed, false if the view was not a header
15162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     *         view
15262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     */
15362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public boolean removeHeaderView(View v) {
15462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        if (mHeaderViewInfos.size() > 0) {
15562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            boolean result = false;
15662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            ListAdapter adapter = getAdapter();
15762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {
15862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                result = true;
15962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
16062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            removeFixedViewInfo(v, mHeaderViewInfos);
16162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return result;
16262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
16362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        return false;
16462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
16562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
16662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
16762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        int len = where.size();
16862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        for (int i = 0; i < len; ++i) {
16962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            FixedViewInfo info = where.get(i);
17062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (info.view == v) {
17162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                where.remove(i);
17262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                break;
17362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
17462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
17562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
17662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
17762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    @Override
17862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    public void setAdapter(ListAdapter adapter) {
17962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        if (mHeaderViewInfos.size() > 0) {
18062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            HeaderViewGridAdapter hadapter = new HeaderViewGridAdapter(mHeaderViewInfos, adapter);
18162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numColumns = getNumColumns();
18262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (numColumns > 1) {
18362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                hadapter.setNumColumns(numColumns);
18462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
18562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            super.setAdapter(hadapter);
18662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        } else {
18762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            super.setAdapter(adapter);
18862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
18962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
19062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
19162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private class FullWidthFixedViewLayout extends FrameLayout {
19262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public FullWidthFixedViewLayout(Context context) {
19362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            super(context);
19462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
19562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
19662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
19762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
19862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int targetWidth = HeaderGridView.this.getMeasuredWidth()
19962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    - HeaderGridView.this.getPaddingLeft()
20062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    - HeaderGridView.this.getPaddingRight();
20162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
20262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    MeasureSpec.getMode(widthMeasureSpec));
20362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
20462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
20562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
20662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
20762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    /**
20862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * ListAdapter used when a HeaderGridView has header views. This ListAdapter
20962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * wraps another one and also keeps track of the header views and their
21062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * associated data objects.
21162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     *<p>This is intended as a base class; you will probably not need to
21262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     * use this class directly in your own code.
21362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu     */
21462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {
21562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
21662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        // This is used to notify the container of updates relating to number of columns
21762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        // or headers changing, which changes the number of placeholders needed
21862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        private final DataSetObservable mDataSetObservable = new DataSetObservable();
21962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
22062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        private final ListAdapter mAdapter;
22162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        private int mNumColumns = 1;
22262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
22362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        // This ArrayList is assumed to NOT be null.
22462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        ArrayList<FixedViewInfo> mHeaderViewInfos;
22562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
22662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        boolean mAreAllFixedViewsSelectable;
22762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
22862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        private final boolean mIsFilterable;
22962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
23062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ListAdapter adapter) {
23162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mAdapter = adapter;
23262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mIsFilterable = adapter instanceof Filterable;
23362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
23462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (headerViewInfos == null) {
23562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                throw new IllegalArgumentException("headerViewInfos cannot be null");
23662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
23762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mHeaderViewInfos = headerViewInfos;
23862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
23962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);
24062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
24162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
24262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public int getHeadersCount() {
24362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return mHeaderViewInfos.size();
24462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
24562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
24662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
24762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean isEmpty() {
24862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0;
24962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
25062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
25162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public void setNumColumns(int numColumns) {
25262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (numColumns < 1) {
25362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                throw new IllegalArgumentException("Number of columns must be 1 or more");
25462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
25562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mNumColumns != numColumns) {
25662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                mNumColumns = numColumns;
25762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                notifyDataSetChanged();
25862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
25962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
26062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
26162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {
26262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (infos != null) {
26362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                for (FixedViewInfo info : infos) {
26462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    if (!info.isSelectable) {
26562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                        return false;
26662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    }
26762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
26862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
26962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return true;
27062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
27162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
27262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean removeHeader(View v) {
27362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            for (int i = 0; i < mHeaderViewInfos.size(); i++) {
27462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                FixedViewInfo info = mHeaderViewInfos.get(i);
27562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (info.view == v) {
27662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    mHeaderViewInfos.remove(i);
27762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
27862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos);
27962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
28062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    mDataSetObservable.notifyChanged();
28162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return true;
28262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
28362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
28462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
28562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return false;
28662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
28762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
28862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
28962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public int getCount() {
29062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
29162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return getHeadersCount() * mNumColumns + mAdapter.getCount();
29262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            } else {
29362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return getHeadersCount() * mNumColumns;
29462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
29562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
29662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
29762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
29862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean areAllItemsEnabled() {
29962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
30062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
30162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            } else {
30262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return true;
30362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
30462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
30562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
30662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
30762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean isEnabled(int position) {
30862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
30962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
31062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (position < numHeadersAndPlaceholders) {
31162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return (position % mNumColumns == 0)
31262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                        && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
31362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
31462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
31562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Adapter
31662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            final int adjPosition = position - numHeadersAndPlaceholders;
31762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int adapterCount = 0;
31862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
31962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                adapterCount = mAdapter.getCount();
32062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (adjPosition < adapterCount) {
32162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mAdapter.isEnabled(adjPosition);
32262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
32362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
32462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
32562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            throw new ArrayIndexOutOfBoundsException(position);
32662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
32762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
32862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
32962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public Object getItem(int position) {
33062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
33162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
33262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (position < numHeadersAndPlaceholders) {
33362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (position % mNumColumns == 0) {
33462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mHeaderViewInfos.get(position / mNumColumns).data;
33562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
33662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return null;
33762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
33862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
33962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Adapter
34062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            final int adjPosition = position - numHeadersAndPlaceholders;
34162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int adapterCount = 0;
34262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
34362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                adapterCount = mAdapter.getCount();
34462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (adjPosition < adapterCount) {
34562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mAdapter.getItem(adjPosition);
34662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
34762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
34862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
34962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            throw new ArrayIndexOutOfBoundsException(position);
35062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
35162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
35262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
35362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public long getItemId(int position) {
35462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
35562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null && position >= numHeadersAndPlaceholders) {
35662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                int adjPosition = position - numHeadersAndPlaceholders;
35762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                int adapterCount = mAdapter.getCount();
35862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (adjPosition < adapterCount) {
35962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mAdapter.getItemId(adjPosition);
36062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
36162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
36262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return -1;
36362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
36462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
36562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
36662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public boolean hasStableIds() {
36762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
36862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return mAdapter.hasStableIds();
36962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
37062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return false;
37162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
37262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
37362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
37462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public View getView(int position, View convertView, ViewGroup parent) {
37562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
37662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns ;
37762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (position < numHeadersAndPlaceholders) {
37862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                View headerViewContainer = mHeaderViewInfos
37962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                        .get(position / mNumColumns).viewContainer;
38062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (position % mNumColumns == 0) {
38162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return headerViewContainer;
38262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                } else {
38362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    if (convertView == null) {
38462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                        convertView = new View(parent.getContext());
38562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    }
38662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    // We need to do this because GridView uses the height of the last item
38762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    // in a row to determine the height for the entire row.
38862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    convertView.setVisibility(View.INVISIBLE);
38962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    convertView.setMinimumHeight(headerViewContainer.getHeight());
39062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return convertView;
39162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
39262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
39362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
39462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            // Adapter
39562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            final int adjPosition = position - numHeadersAndPlaceholders;
39662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int adapterCount = 0;
39762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
39862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                adapterCount = mAdapter.getCount();
39962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (adjPosition < adapterCount) {
40062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mAdapter.getView(adjPosition, convertView, parent);
40162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
40262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
40362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
40462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            throw new ArrayIndexOutOfBoundsException(position);
40562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
40662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
40762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
40862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public int getItemViewType(int position) {
40962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
41062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (position < numHeadersAndPlaceholders && (position % mNumColumns != 0)) {
41162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                // Placeholders get the last view type number
41262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return mAdapter != null ? mAdapter.getViewTypeCount() : 1;
41362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
41462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null && position >= numHeadersAndPlaceholders) {
41562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                int adjPosition = position - numHeadersAndPlaceholders;
41662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                int adapterCount = mAdapter.getCount();
41762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                if (adjPosition < adapterCount) {
41862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                    return mAdapter.getItemViewType(adjPosition);
41962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                }
42062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
42162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
42262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
42362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
42462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
42562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
42662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public int getViewTypeCount() {
42762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
42862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return mAdapter.getViewTypeCount() + 1;
42962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
43062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return 2;
43162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
43262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
43362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
43462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public void registerDataSetObserver(DataSetObserver observer) {
43562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mDataSetObservable.registerObserver(observer);
43662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
43762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                mAdapter.registerDataSetObserver(observer);
43862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
43962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
44062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
44162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
44262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public void unregisterDataSetObserver(DataSetObserver observer) {
44362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mDataSetObservable.unregisterObserver(observer);
44462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mAdapter != null) {
44562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                mAdapter.unregisterDataSetObserver(observer);
44662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
44762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
44862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
44962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
45062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public Filter getFilter() {
45162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            if (mIsFilterable) {
45262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu                return ((Filterable) mAdapter).getFilter();
45362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            }
45462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return null;
45562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
45662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
45762cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        @Override
45862cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public ListAdapter getWrappedAdapter() {
45962cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            return mAdapter;
46062cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
46162cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu
46262cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        public void notifyDataSetChanged() {
46362cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu            mDataSetObservable.notifyChanged();
46462cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu        }
46562cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu    }
46662cc2aa4400a4ad03cfc6025e9d25c61015cdef4Bobby Georgescu}
467