119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo/*
219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Copyright (C) 2011 Google Inc.
319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Licensed to The Android Open Source Project.
419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Licensed under the Apache License, Version 2.0 (the "License");
619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * you may not use this file except in compliance with the License.
719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * You may obtain a copy of the License at
819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *      http://www.apache.org/licenses/LICENSE-2.0
1019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
1119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Unless required by applicable law or agreed to in writing, software
1219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * distributed under the License is distributed on an "AS IS" BASIS,
1319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * See the License for the specific language governing permissions and
1519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * limitations under the License.
1619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */
1719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
1819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
1919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalopackage com.android.mailcommon;
2019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
2119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport com.android.mailcommon.MergedAdapter.ListSpinnerAdapter;
2219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport com.android.mailcommon.MergedAdapter.LocalAdapterPosition;
2319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
2419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.content.Context;
25d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereiraimport android.graphics.Rect;
26d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereiraimport android.graphics.drawable.Drawable;
2719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.util.AttributeSet;
2819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.view.View;
2919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.view.ViewGroup;
3019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.AdapterView;
3119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.FrameLayout;
3219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.ListPopupWindow;
3319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.ListView;
3419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
3519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
3619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo/**
3719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>A spinner-like widget that combines data and views from multiple adapters (via MergedAdapter)
3819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * and forwards certain events to those adapters. This widget also supports clickable but
3919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * unselectable dropdown items, useful when displaying extra items that should not affect spinner
4019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * selection state.</p>
4119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
4219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>The framework's Spinner widget can't be extended for this task because it uses a private list
4319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * adapter (which prevents setting multiple item types) and hides access to its popup, which is
4419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * useful for clients to know about (like when it's been opened).</p>
4519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
4619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>Clients must provide a set of adapters which the widget will use to load dropdown views,
4719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * receive callbacks, and load the selected item's view.</p>
4819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
4919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>Apps incorporating this widget must declare a custom attribute: "dropDownWidth" under the
5019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * "MultiAdapterSpinner" name as a "reference" format (see Gmail's attrs.xml file for an
5119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * example). This attribute controls the width of the dropdown, similar to the attribute in the
5219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * framework's Spinner widget.</p>
5319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo *
5419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */
5519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalopublic class MultiAdapterSpinner extends FrameLayout
5619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    implements AdapterView.OnItemClickListener, View.OnClickListener {
5719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
582859eb0fec820a0677dce9a72f49601f10592a62Andy Huang    protected MergedAdapter<FancySpinnerAdapter> mAdapter;
5919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    protected ListPopupWindow mPopup;
6019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
61f20088d987deb48f185f5725426db0a0c4c26918Andy Huang    private int mSelectedPosition = -1;
62d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira    private Rect mTempRect = new Rect();
6319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
6419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    /**
6519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo     * A basic adapter with some callbacks added so clients can be involved in spinner behavior.
6619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo     */
6719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public interface FancySpinnerAdapter extends ListSpinnerAdapter {
6819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        /**
6919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         * Whether or not an item at position should become the new selected spinner item and change
7019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         * the spinner item view.
7119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         */
7219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        boolean canSelect(int position);
7319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        /**
7419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         * Handle a click on an enabled item.
7519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         */
7619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        void onClick(int position);
7719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        /**
7819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         * Fired when the popup window is about to be displayed.
7919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         */
8019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        void onShowPopup();
8119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
8219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
832859eb0fec820a0677dce9a72f49601f10592a62Andy Huang    private static class MergedSpinnerAdapter extends MergedAdapter<FancySpinnerAdapter> {
8419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        /**
8519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         * ListPopupWindow uses getView() but spinners return dropdown views in getDropDownView().
8619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo         */
8719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        @Override
8819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        public View getView(int position, View convertView, ViewGroup parent) {
8919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            return super.getDropDownView(position, convertView, parent);
9019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        }
9119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
9219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
9319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public MultiAdapterSpinner(Context context) {
9419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        this(context, null);
9519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
9619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
9719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public MultiAdapterSpinner(Context context, AttributeSet attrs) {
9819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        super(context, attrs);
9919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
10019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mAdapter = new MergedSpinnerAdapter();
10119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup = new ListPopupWindow(context, attrs);
10219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup.setAnchorView(this);
10319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup.setOnItemClickListener(this);
10419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup.setModal(true);
10519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup.setAdapter(mAdapter);
10619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
10719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
10819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public void setAdapters(FancySpinnerAdapter... adapters) {
10919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mAdapter.setAdapters(adapters);
11019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
11119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
11219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public void setSelectedItem(final FancySpinnerAdapter adapter, final int position) {
11319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        int globalPosition = 0;
11419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        boolean found = false;
11519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
11619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
11719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            ListSpinnerAdapter a = mAdapter.getSubAdapter(i);
11819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            if (a == adapter) {
11919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                globalPosition += position;
12019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                found = true;
12119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                break;
12219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            }
12319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            globalPosition += a.getCount();
12419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        }
12519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        if (found) {
12619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            if (adapter.canSelect(position)) {
12719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                removeAllViews();
12819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                View itemView = adapter.getView(position, null, this);
12919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                itemView.setClickable(true);
13019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                itemView.setOnClickListener(this);
13119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                addView(itemView);
13219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
13319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                if (position < adapter.getCount()) {
13419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                    mSelectedPosition = globalPosition;
13519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                }
13619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            }
13719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        }
13819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
13919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
14019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    @Override
14119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public void onClick(View v) {
14219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        if (!mPopup.isShowing()) {
14319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
14419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
14519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                mAdapter.getSubAdapter(i).onShowPopup();
14619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            }
14719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
148d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            final int spinnerPaddingLeft = getPaddingLeft();
149d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            final Drawable background = mPopup.getBackground();
150d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            int bgOffset = 0;
151d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            if (background != null) {
152d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira                background.getPadding(mTempRect);
153d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira                bgOffset = -mTempRect.left;
154d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            }
155d48018c1345a557221f2f2a36e8507ff891b8bf5Mindy Pereira            mPopup.setHorizontalOffset(bgOffset + spinnerPaddingLeft);
15619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            mPopup.show();
15719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            mPopup.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
15819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            mPopup.setSelection(mSelectedPosition);
15919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        }
16019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
16119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
16219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    @Override
16319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
16419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
16519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        if (position != mSelectedPosition) {
16619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            final LocalAdapterPosition<FancySpinnerAdapter> result =
16719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                mAdapter.getAdapterOffsetForItem(position);
16819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
16919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            if (result.mAdapter.canSelect(result.mLocalPosition)) {
17019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                mSelectedPosition = position;
17119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            } else {
17219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                mPopup.clearListSelection();
17319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            }
17419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
17519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            post(new Runnable() {
17619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                @Override
17719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                public void run() {
17819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                    result.mAdapter.onClick(result.mLocalPosition);
17919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo                }
18019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo            });
18119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        }
18219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
18319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo        mPopup.dismiss();
18419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo    }
18519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo
18619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo}
187