1415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/*
2415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Copyright (C) 2014 The Android Open Source Project
3415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
4415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * you may not use this file except in compliance with the License.
6415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * You may obtain a copy of the License at
7415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
8415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
10415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Unless required by applicable law or agreed to in writing, software
11415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * See the License for the specific language governing permissions and
14415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * limitations under the License.
15415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */
16415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
172cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banespackage android.support.v7.widget;
18415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
19415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.Context;
20a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.content.res.ColorStateList;
21f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.Resources;
22f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.TypedArray;
23f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.database.DataSetObserver;
24a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.graphics.PorterDuff;
25f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.graphics.Rect;
260332c97857f4aa1071e2e01875129ca3bea6c052Chris Banesimport android.graphics.drawable.Drawable;
27415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.os.Build;
28091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.annotation.DrawableRes;
29a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.support.annotation.Nullable;
30c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
31a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.support.v4.view.TintableBackgroundView;
32f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.support.v4.view.ViewCompat;
332cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banesimport android.support.v7.appcompat.R;
344c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport android.support.v7.content.res.AppCompatResources;
3566698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport android.support.v7.view.ContextThemeWrapper;
366142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v7.view.menu.ShowableListMenu;
37415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.util.AttributeSet;
38f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.util.Log;
39f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.MotionEvent;
40f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.View;
41f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.ViewGroup;
42f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.ViewTreeObserver;
43f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.AdapterView;
4498e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banesimport android.widget.ArrayAdapter;
45f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.ListAdapter;
46f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.ListView;
47f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.PopupWindow;
48415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.widget.Spinner;
49f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.SpinnerAdapter;
50415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
51c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID;
52c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette
530332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes
54415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/**
551d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * A {@link Spinner} which supports compatible features on older versions of the platform,
56091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * including:
57091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * <ul>
581d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Dynamic tinting of the background via the background tint methods in
59f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link android.support.v4.view.ViewCompat}.</li>
601d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Configuring the background tint using {@link R.attr#backgroundTint} and
61f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link R.attr#backgroundTintMode}.</li>
621d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Setting the popup theme using {@link R.attr#popupTheme}.</li>
63091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * </ul>
64091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
65091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * <p>This will automatically be used when you use {@link Spinner} in your layouts.
66091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * You should only need to manually use this class when writing custom views.</p>
67415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */
682cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banespublic class AppCompatSpinner extends Spinner implements TintableBackgroundView {
69415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
702c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    static final boolean IS_AT_LEAST_M = Build.VERSION.SDK_INT >= 23;
71f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final boolean IS_AT_LEAST_JB = Build.VERSION.SDK_INT >= 16;
72f933672168f6deff5e3636584bec735416b5a93bChris Banes
73f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final int[] ATTRS_ANDROID_SPINNERMODE = {android.R.attr.spinnerMode};
74f933672168f6deff5e3636584bec735416b5a93bChris Banes
75f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final int MAX_ITEMS_MEASURED = 15;
76f933672168f6deff5e3636584bec735416b5a93bChris Banes
77f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final String TAG = "AppCompatSpinner";
78f933672168f6deff5e3636584bec735416b5a93bChris Banes
79f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final int MODE_DIALOG = 0;
80f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final int MODE_DROPDOWN = 1;
81f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static final int MODE_THEME = -1;
82415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
83091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private AppCompatBackgroundHelper mBackgroundTintHelper;
84a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
85f933672168f6deff5e3636584bec735416b5a93bChris Banes    /** Context used to inflate the popup window or dialog. */
86f933672168f6deff5e3636584bec735416b5a93bChris Banes    private Context mPopupContext;
87f933672168f6deff5e3636584bec735416b5a93bChris Banes
88f933672168f6deff5e3636584bec735416b5a93bChris Banes    /** Forwarding listener used to implement drag-to-open. */
896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes    private ForwardingListener mForwardingListener;
90f933672168f6deff5e3636584bec735416b5a93bChris Banes
91f933672168f6deff5e3636584bec735416b5a93bChris Banes    /** Temporary holder for setAdapter() calls from the super constructor. */
92f933672168f6deff5e3636584bec735416b5a93bChris Banes    private SpinnerAdapter mTempAdapter;
93f933672168f6deff5e3636584bec735416b5a93bChris Banes
94f933672168f6deff5e3636584bec735416b5a93bChris Banes    private boolean mPopupSet;
95f933672168f6deff5e3636584bec735416b5a93bChris Banes
962c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    DropdownPopup mPopup;
97f933672168f6deff5e3636584bec735416b5a93bChris Banes
982c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    int mDropDownWidth;
99f933672168f6deff5e3636584bec735416b5a93bChris Banes
1002c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    final Rect mTempRect = new Rect();
101f933672168f6deff5e3636584bec735416b5a93bChris Banes
102f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
103f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Construct a new spinner with the given context's theme.
104f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
105f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context The Context the view is running in, through which it can
106f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                access the current theme, resources, etc.
107f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
1082cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes    public AppCompatSpinner(Context context) {
109415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        this(context, null);
110415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
112f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
113f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Construct a new spinner with the given context's theme and the supplied
114f933672168f6deff5e3636584bec735416b5a93bChris Banes     * mode of displaying choices. <code>mode</code> may be one of
115f933672168f6deff5e3636584bec735416b5a93bChris Banes     * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}.
116f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
117f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context The Context the view is running in, through which it can
118f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                access the current theme, resources, etc.
119f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param mode    Constant describing how the user will select choices from the spinner.
120f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DIALOG
121f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DROPDOWN
122f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
123f933672168f6deff5e3636584bec735416b5a93bChris Banes    public AppCompatSpinner(Context context, int mode) {
124f933672168f6deff5e3636584bec735416b5a93bChris Banes        this(context, null, R.attr.spinnerStyle, mode);
125f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
126f933672168f6deff5e3636584bec735416b5a93bChris Banes
127f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
128f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Construct a new spinner with the given context's theme and the supplied attribute set.
129f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
130f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context The Context the view is running in, through which it can
131f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                access the current theme, resources, etc.
132f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param attrs   The attributes of the XML tag that is inflating the view.
133f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
1342cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes    public AppCompatSpinner(Context context, AttributeSet attrs) {
1352cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes        this(context, attrs, R.attr.spinnerStyle);
136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
138f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
139f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Construct a new spinner with the given context's theme, the supplied attribute set,
140f933672168f6deff5e3636584bec735416b5a93bChris Banes     * and default style attribute.
141f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
142f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context      The Context the view is running in, through which it can
143f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     access the current theme, resources, etc.
144f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param attrs        The attributes of the XML tag that is inflating the view.
145f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param defStyleAttr An attribute in the current theme that contains a
146f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     reference to a style resource that supplies default values for
147f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     the view. Can be 0 to not look for defaults.
148f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
1492cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
150f933672168f6deff5e3636584bec735416b5a93bChris Banes        this(context, attrs, defStyleAttr, MODE_THEME);
151f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
152f933672168f6deff5e3636584bec735416b5a93bChris Banes
153f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
154f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Construct a new spinner with the given context's theme, the supplied attribute set,
155f933672168f6deff5e3636584bec735416b5a93bChris Banes     * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
156f933672168f6deff5e3636584bec735416b5a93bChris Banes     * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
157f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
158f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context      The Context the view is running in, through which it can
159f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     access the current theme, resources, etc.
160f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param attrs        The attributes of the XML tag that is inflating the view.
161f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param defStyleAttr An attribute in the current theme that contains a
162f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     reference to a style resource that supplies default values for
163f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     the view. Can be 0 to not look for defaults.
164f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param mode         Constant describing how the user will select choices from the spinner.
165f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DIALOG
166f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DROPDOWN
167f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
168bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
169bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes        this(context, attrs, defStyleAttr, mode, null);
170f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
171f933672168f6deff5e3636584bec735416b5a93bChris Banes
172f933672168f6deff5e3636584bec735416b5a93bChris Banes
173f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
174f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Constructs a new spinner with the given context's theme, the supplied
175f933672168f6deff5e3636584bec735416b5a93bChris Banes     * attribute set, default styles, popup mode (one of {@link #MODE_DIALOG}
176f933672168f6deff5e3636584bec735416b5a93bChris Banes     * or {@link #MODE_DROPDOWN}), and the context against which the popup
177f933672168f6deff5e3636584bec735416b5a93bChris Banes     * should be inflated.
178f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
179f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param context      The context against which the view is inflated, which
180f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     provides access to the current theme, resources, etc.
181f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param attrs        The attributes of the XML tag that is inflating the view.
182f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param defStyleAttr An attribute in the current theme that contains a
183f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     reference to a style resource that supplies default
184f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     values for the view. Can be 0 to not look for
185f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     defaults.
186f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param mode         Constant describing how the user will select choices from
187f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     the spinner.
188f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param popupTheme   The theme against which the dialog or dropdown popup
189f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     should be inflated. May be {@code null} to use the
190f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     view theme. If set, this will override any value
191f933672168f6deff5e3636584bec735416b5a93bChris Banes     *                     specified by
192bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes     *                     {@link R.styleable#Spinner_popupTheme}.
193f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DIALOG
194f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see #MODE_DROPDOWN
195f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
196f933672168f6deff5e3636584bec735416b5a93bChris Banes    public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode,
197f933672168f6deff5e3636584bec735416b5a93bChris Banes            Resources.Theme popupTheme) {
198415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super(context, attrs, defStyleAttr);
199415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
200f933672168f6deff5e3636584bec735416b5a93bChris Banes        TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
201f933672168f6deff5e3636584bec735416b5a93bChris Banes                R.styleable.Spinner, defStyleAttr, 0);
202f933672168f6deff5e3636584bec735416b5a93bChris Banes
2034c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        mBackgroundTintHelper = new AppCompatBackgroundHelper(this);
204091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
205f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (popupTheme != null) {
206f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopupContext = new ContextThemeWrapper(context, popupTheme);
207f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else {
208f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int popupThemeResId = a.getResourceId(R.styleable.Spinner_popupTheme, 0);
209f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (popupThemeResId != 0) {
210f933672168f6deff5e3636584bec735416b5a93bChris Banes                mPopupContext = new ContextThemeWrapper(context, popupThemeResId);
211f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
212f933672168f6deff5e3636584bec735416b5a93bChris Banes                // If we're running on a < M device, we'll use the current context and still handle
213f933672168f6deff5e3636584bec735416b5a93bChris Banes                // any dropdown popup
214f933672168f6deff5e3636584bec735416b5a93bChris Banes                mPopupContext = !IS_AT_LEAST_M ? context : null;
215f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
216f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
217f933672168f6deff5e3636584bec735416b5a93bChris Banes
218f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopupContext != null) {
219f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (mode == MODE_THEME) {
220f933672168f6deff5e3636584bec735416b5a93bChris Banes                if (Build.VERSION.SDK_INT >= 11) {
221f933672168f6deff5e3636584bec735416b5a93bChris Banes                    // If we're running on API v11+ we will try and read android:spinnerMode
222f933672168f6deff5e3636584bec735416b5a93bChris Banes                    TypedArray aa = null;
223f933672168f6deff5e3636584bec735416b5a93bChris Banes                    try {
224f933672168f6deff5e3636584bec735416b5a93bChris Banes                        aa = context.obtainStyledAttributes(attrs, ATTRS_ANDROID_SPINNERMODE,
225f933672168f6deff5e3636584bec735416b5a93bChris Banes                                defStyleAttr, 0);
226f933672168f6deff5e3636584bec735416b5a93bChris Banes                        if (aa.hasValue(0)) {
227f933672168f6deff5e3636584bec735416b5a93bChris Banes                            mode = aa.getInt(0, MODE_DIALOG);
228f933672168f6deff5e3636584bec735416b5a93bChris Banes                        }
229f933672168f6deff5e3636584bec735416b5a93bChris Banes                    } catch (Exception e) {
230f933672168f6deff5e3636584bec735416b5a93bChris Banes                        Log.i(TAG, "Could not read android:spinnerMode", e);
231f933672168f6deff5e3636584bec735416b5a93bChris Banes                    } finally {
232f933672168f6deff5e3636584bec735416b5a93bChris Banes                        if (aa != null) {
233f933672168f6deff5e3636584bec735416b5a93bChris Banes                            aa.recycle();
234f933672168f6deff5e3636584bec735416b5a93bChris Banes                        }
235f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
236f933672168f6deff5e3636584bec735416b5a93bChris Banes                } else {
237f933672168f6deff5e3636584bec735416b5a93bChris Banes                    // Else, we use a default mode of dropdown
238f933672168f6deff5e3636584bec735416b5a93bChris Banes                    mode = MODE_DROPDOWN;
239f933672168f6deff5e3636584bec735416b5a93bChris Banes                }
240f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
241f933672168f6deff5e3636584bec735416b5a93bChris Banes
242f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (mode == MODE_DROPDOWN) {
243f933672168f6deff5e3636584bec735416b5a93bChris Banes                final DropdownPopup popup = new DropdownPopup(mPopupContext, attrs, defStyleAttr);
244f933672168f6deff5e3636584bec735416b5a93bChris Banes                final TintTypedArray pa = TintTypedArray.obtainStyledAttributes(
245f933672168f6deff5e3636584bec735416b5a93bChris Banes                        mPopupContext, attrs, R.styleable.Spinner, defStyleAttr, 0);
246f933672168f6deff5e3636584bec735416b5a93bChris Banes                mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_android_dropDownWidth,
247f933672168f6deff5e3636584bec735416b5a93bChris Banes                        LayoutParams.WRAP_CONTENT);
248f933672168f6deff5e3636584bec735416b5a93bChris Banes                popup.setBackgroundDrawable(
249f933672168f6deff5e3636584bec735416b5a93bChris Banes                        pa.getDrawable(R.styleable.Spinner_android_popupBackground));
250f933672168f6deff5e3636584bec735416b5a93bChris Banes                popup.setPromptText(a.getString(R.styleable.Spinner_android_prompt));
251f933672168f6deff5e3636584bec735416b5a93bChris Banes                pa.recycle();
252f933672168f6deff5e3636584bec735416b5a93bChris Banes
253f933672168f6deff5e3636584bec735416b5a93bChris Banes                mPopup = popup;
2546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                mForwardingListener = new ForwardingListener(this) {
255f933672168f6deff5e3636584bec735416b5a93bChris Banes                    @Override
2566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes                    public ShowableListMenu getPopup() {
257f933672168f6deff5e3636584bec735416b5a93bChris Banes                        return popup;
258f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
259f933672168f6deff5e3636584bec735416b5a93bChris Banes
260f933672168f6deff5e3636584bec735416b5a93bChris Banes                    @Override
261f933672168f6deff5e3636584bec735416b5a93bChris Banes                    public boolean onForwardingStarted() {
262f933672168f6deff5e3636584bec735416b5a93bChris Banes                        if (!mPopup.isShowing()) {
263f933672168f6deff5e3636584bec735416b5a93bChris Banes                            mPopup.show();
264f933672168f6deff5e3636584bec735416b5a93bChris Banes                        }
265f933672168f6deff5e3636584bec735416b5a93bChris Banes                        return true;
266f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
267f933672168f6deff5e3636584bec735416b5a93bChris Banes                };
268fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes            }
269fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
27098e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes
27198e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes        final CharSequence[] entries = a.getTextArray(R.styleable.Spinner_android_entries);
27298e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes        if (entries != null) {
273c9668b98d83e8a8696685c6c36be7643ab1e25e0Chris Banes            final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<>(
274c9668b98d83e8a8696685c6c36be7643ab1e25e0Chris Banes                    context, android.R.layout.simple_spinner_item, entries);
27598e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes            adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
27698e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes            setAdapter(adapter);
27798e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes        }
27898e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes
279091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        a.recycle();
280415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
281f933672168f6deff5e3636584bec735416b5a93bChris Banes        mPopupSet = true;
282f933672168f6deff5e3636584bec735416b5a93bChris Banes
283f933672168f6deff5e3636584bec735416b5a93bChris Banes        // Base constructors can call setAdapter before we initialize mPopup.
284f933672168f6deff5e3636584bec735416b5a93bChris Banes        // Finish setting things up if this happened.
285f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mTempAdapter != null) {
286f933672168f6deff5e3636584bec735416b5a93bChris Banes            setAdapter(mTempAdapter);
287f933672168f6deff5e3636584bec735416b5a93bChris Banes            mTempAdapter = null;
288f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
289f933672168f6deff5e3636584bec735416b5a93bChris Banes
290091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr);
291f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
292f933672168f6deff5e3636584bec735416b5a93bChris Banes
293f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
294f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @return the context used to inflate the Spinner's popup or dialog window
295f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
296f933672168f6deff5e3636584bec735416b5a93bChris Banes    public Context getPopupContext() {
297f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
298f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mPopupContext;
299f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_M) {
300f933672168f6deff5e3636584bec735416b5a93bChris Banes            return super.getPopupContext();
301f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
302f933672168f6deff5e3636584bec735416b5a93bChris Banes        return null;
303f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
304684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes
305f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setPopupBackgroundDrawable(Drawable background) {
306f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
307f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.setBackgroundDrawable(background);
308f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
309f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setPopupBackgroundDrawable(background);
310f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
311684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes    }
312684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes
313f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setPopupBackgroundResource(@DrawableRes int resId) {
3144c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        setPopupBackgroundDrawable(AppCompatResources.getDrawable(getPopupContext(), resId));
315f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
3160332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes
317f933672168f6deff5e3636584bec735416b5a93bChris Banes    public Drawable getPopupBackground() {
318f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
319f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mPopup.getBackground();
320f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
321f933672168f6deff5e3636584bec735416b5a93bChris Banes            return super.getPopupBackground();
322f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
323f933672168f6deff5e3636584bec735416b5a93bChris Banes        return null;
324f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
3250332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes
326f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setDropDownVerticalOffset(int pixels) {
327f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
328f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.setVerticalOffset(pixels);
329f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
330f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setDropDownVerticalOffset(pixels);
331f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
332f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
333f933672168f6deff5e3636584bec735416b5a93bChris Banes
334f933672168f6deff5e3636584bec735416b5a93bChris Banes    public int getDropDownVerticalOffset() {
335f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
336f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mPopup.getVerticalOffset();
337f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
338f933672168f6deff5e3636584bec735416b5a93bChris Banes            return super.getDropDownVerticalOffset();
339f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
340f933672168f6deff5e3636584bec735416b5a93bChris Banes        return 0;
341f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
342f933672168f6deff5e3636584bec735416b5a93bChris Banes
343f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setDropDownHorizontalOffset(int pixels) {
344f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
345f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.setHorizontalOffset(pixels);
346f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
347f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setDropDownHorizontalOffset(pixels);
348f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
349f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
350f933672168f6deff5e3636584bec735416b5a93bChris Banes
351f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
352f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Get the configured horizontal offset in pixels for the spinner's popup window of choices.
353f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Only valid in {@link #MODE_DROPDOWN}; other modes will return 0.
354f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
355f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @return Horizontal offset in pixels
356f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
357f933672168f6deff5e3636584bec735416b5a93bChris Banes    public int getDropDownHorizontalOffset() {
358f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
359f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mPopup.getHorizontalOffset();
360f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
361f933672168f6deff5e3636584bec735416b5a93bChris Banes            return super.getDropDownHorizontalOffset();
362f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
363f933672168f6deff5e3636584bec735416b5a93bChris Banes        return 0;
364f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
365f933672168f6deff5e3636584bec735416b5a93bChris Banes
366f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setDropDownWidth(int pixels) {
367f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
368f933672168f6deff5e3636584bec735416b5a93bChris Banes            mDropDownWidth = pixels;
369f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
370f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setDropDownWidth(pixels);
371f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
372f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
373f933672168f6deff5e3636584bec735416b5a93bChris Banes
374f933672168f6deff5e3636584bec735416b5a93bChris Banes    public int getDropDownWidth() {
375f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
376f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mDropDownWidth;
377f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else if (IS_AT_LEAST_JB) {
378f933672168f6deff5e3636584bec735416b5a93bChris Banes            return super.getDropDownWidth();
379f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
380f933672168f6deff5e3636584bec735416b5a93bChris Banes        return 0;
381f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
382f933672168f6deff5e3636584bec735416b5a93bChris Banes
383f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
384f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setAdapter(SpinnerAdapter adapter) {
385f933672168f6deff5e3636584bec735416b5a93bChris Banes        // The super constructor may call setAdapter before we're prepared.
386f933672168f6deff5e3636584bec735416b5a93bChris Banes        // Postpone doing anything until we've finished construction.
387f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (!mPopupSet) {
388f933672168f6deff5e3636584bec735416b5a93bChris Banes            mTempAdapter = adapter;
389f933672168f6deff5e3636584bec735416b5a93bChris Banes            return;
390f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
391f933672168f6deff5e3636584bec735416b5a93bChris Banes
392f933672168f6deff5e3636584bec735416b5a93bChris Banes        super.setAdapter(adapter);
393f933672168f6deff5e3636584bec735416b5a93bChris Banes
394f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
395f933672168f6deff5e3636584bec735416b5a93bChris Banes            final Context popupContext = mPopupContext == null ? getContext() : mPopupContext;
396f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
3970332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes        }
3980332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes    }
3990332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes
400091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    @Override
401f933672168f6deff5e3636584bec735416b5a93bChris Banes    protected void onDetachedFromWindow() {
402f933672168f6deff5e3636584bec735416b5a93bChris Banes        super.onDetachedFromWindow();
403f933672168f6deff5e3636584bec735416b5a93bChris Banes
404f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null && mPopup.isShowing()) {
405f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.dismiss();
406f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
407f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
408f933672168f6deff5e3636584bec735416b5a93bChris Banes
409f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
410f933672168f6deff5e3636584bec735416b5a93bChris Banes    public boolean onTouchEvent(MotionEvent event) {
411f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mForwardingListener != null && mForwardingListener.onTouch(this, event)) {
412f933672168f6deff5e3636584bec735416b5a93bChris Banes            return true;
413f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
414f933672168f6deff5e3636584bec735416b5a93bChris Banes        return super.onTouchEvent(event);
415f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
416f933672168f6deff5e3636584bec735416b5a93bChris Banes
417f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
418f933672168f6deff5e3636584bec735416b5a93bChris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
419f933672168f6deff5e3636584bec735416b5a93bChris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
420f933672168f6deff5e3636584bec735416b5a93bChris Banes
421f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
422f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int measuredWidth = getMeasuredWidth();
423f933672168f6deff5e3636584bec735416b5a93bChris Banes            setMeasuredDimension(Math.min(Math.max(measuredWidth,
424f933672168f6deff5e3636584bec735416b5a93bChris Banes                                    compatMeasureContentWidth(getAdapter(), getBackground())),
425f933672168f6deff5e3636584bec735416b5a93bChris Banes                            MeasureSpec.getSize(widthMeasureSpec)),
426f933672168f6deff5e3636584bec735416b5a93bChris Banes                    getMeasuredHeight());
427f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
428f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
429f933672168f6deff5e3636584bec735416b5a93bChris Banes
430f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
431f933672168f6deff5e3636584bec735416b5a93bChris Banes    public boolean performClick() {
43269a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes        if (mPopup != null) {
43369a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes            // If we have a popup, show it if needed, or just consume the click...
43469a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes            if (!mPopup.isShowing()) {
43569a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes                mPopup.show();
43669a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes            }
437f933672168f6deff5e3636584bec735416b5a93bChris Banes            return true;
438f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
43969a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes
44069a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes        // Else let the platform handle the click
441f933672168f6deff5e3636584bec735416b5a93bChris Banes        return super.performClick();
442f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
443f933672168f6deff5e3636584bec735416b5a93bChris Banes
444f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
445f933672168f6deff5e3636584bec735416b5a93bChris Banes    public void setPrompt(CharSequence prompt) {
446f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (mPopup != null) {
447f933672168f6deff5e3636584bec735416b5a93bChris Banes            mPopup.setPromptText(prompt);
448f933672168f6deff5e3636584bec735416b5a93bChris Banes        } else {
449f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setPrompt(prompt);
450f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
451f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
452f933672168f6deff5e3636584bec735416b5a93bChris Banes
453f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
454f933672168f6deff5e3636584bec735416b5a93bChris Banes    public CharSequence getPrompt() {
455f933672168f6deff5e3636584bec735416b5a93bChris Banes        return mPopup != null ? mPopup.getHintText() : super.getPrompt();
456f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
457f933672168f6deff5e3636584bec735416b5a93bChris Banes
458f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Override
459091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    public void setBackgroundResource(@DrawableRes int resId) {
460091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        super.setBackgroundResource(resId);
461091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTintHelper != null) {
462091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mBackgroundTintHelper.onSetBackgroundResource(resId);
463091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
464091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
465091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
466091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    @Override
467091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    public void setBackgroundDrawable(Drawable background) {
468091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        super.setBackgroundDrawable(background);
469091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTintHelper != null) {
470091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mBackgroundTintHelper.onSetBackgroundDrawable(background);
471091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
472091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
473091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
474a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    /**
475a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * This should be accessed via
476f933672168f6deff5e3636584bec735416b5a93bChris Banes     * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View,
477f933672168f6deff5e3636584bec735416b5a93bChris Banes     * ColorStateList)}
478a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     *
479a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * @hide
480a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     */
481c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
482a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Override
483a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public void setSupportBackgroundTintList(@Nullable ColorStateList tint) {
484091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTintHelper != null) {
485091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mBackgroundTintHelper.setSupportBackgroundTintList(tint);
486a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
487a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
488a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
489a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    /**
490a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * This should be accessed via
491a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)}
492a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     *
493a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * @hide
494a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     */
495c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
496a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Override
497a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Nullable
498a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public ColorStateList getSupportBackgroundTintList() {
499091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mBackgroundTintHelper != null
500091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                ? mBackgroundTintHelper.getSupportBackgroundTintList() : null;
501a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
502a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
503a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    /**
504a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * This should be accessed via
505f933672168f6deff5e3636584bec735416b5a93bChris Banes     * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View,
506f933672168f6deff5e3636584bec735416b5a93bChris Banes     * PorterDuff.Mode)}
507a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     *
508a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * @hide
509a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     */
510c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
511a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Override
512a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
513091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTintHelper != null) {
514091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode);
515a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        }
516a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
517a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
518a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    /**
519a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * This should be accessed via
520a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)}
521a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     *
522a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     * @hide
523a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes     */
524c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
525a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Override
526a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Nullable
527a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    public PorterDuff.Mode getSupportBackgroundTintMode() {
528091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mBackgroundTintHelper != null
529091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null;
530a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    }
531a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes
532a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    @Override
533a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes    protected void drawableStateChanged() {
534a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes        super.drawableStateChanged();
535091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTintHelper != null) {
536091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mBackgroundTintHelper.applySupportBackgroundTint();
537684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes        }
538684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes    }
539f933672168f6deff5e3636584bec735416b5a93bChris Banes
5402c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    int compatMeasureContentWidth(SpinnerAdapter adapter, Drawable background) {
541f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (adapter == null) {
542f933672168f6deff5e3636584bec735416b5a93bChris Banes            return 0;
543f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
544f933672168f6deff5e3636584bec735416b5a93bChris Banes
545f933672168f6deff5e3636584bec735416b5a93bChris Banes        int width = 0;
546f933672168f6deff5e3636584bec735416b5a93bChris Banes        View itemView = null;
547f933672168f6deff5e3636584bec735416b5a93bChris Banes        int itemType = 0;
548f933672168f6deff5e3636584bec735416b5a93bChris Banes        final int widthMeasureSpec =
549f933672168f6deff5e3636584bec735416b5a93bChris Banes                MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED);
550f933672168f6deff5e3636584bec735416b5a93bChris Banes        final int heightMeasureSpec =
551f933672168f6deff5e3636584bec735416b5a93bChris Banes                MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.UNSPECIFIED);
552f933672168f6deff5e3636584bec735416b5a93bChris Banes
553f933672168f6deff5e3636584bec735416b5a93bChris Banes        // Make sure the number of items we'll measure is capped. If it's a huge data set
554f933672168f6deff5e3636584bec735416b5a93bChris Banes        // with wildly varying sizes, oh well.
555f933672168f6deff5e3636584bec735416b5a93bChris Banes        int start = Math.max(0, getSelectedItemPosition());
556f933672168f6deff5e3636584bec735416b5a93bChris Banes        final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED);
557f933672168f6deff5e3636584bec735416b5a93bChris Banes        final int count = end - start;
558f933672168f6deff5e3636584bec735416b5a93bChris Banes        start = Math.max(0, start - (MAX_ITEMS_MEASURED - count));
559f933672168f6deff5e3636584bec735416b5a93bChris Banes        for (int i = start; i < end; i++) {
560f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int positionType = adapter.getItemViewType(i);
561f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (positionType != itemType) {
562f933672168f6deff5e3636584bec735416b5a93bChris Banes                itemType = positionType;
563f933672168f6deff5e3636584bec735416b5a93bChris Banes                itemView = null;
564f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
565f933672168f6deff5e3636584bec735416b5a93bChris Banes            itemView = adapter.getView(i, itemView, this);
566f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (itemView.getLayoutParams() == null) {
567f933672168f6deff5e3636584bec735416b5a93bChris Banes                itemView.setLayoutParams(new LayoutParams(
568f933672168f6deff5e3636584bec735416b5a93bChris Banes                        LayoutParams.WRAP_CONTENT,
569f933672168f6deff5e3636584bec735416b5a93bChris Banes                        LayoutParams.WRAP_CONTENT));
570f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
571f933672168f6deff5e3636584bec735416b5a93bChris Banes            itemView.measure(widthMeasureSpec, heightMeasureSpec);
572f933672168f6deff5e3636584bec735416b5a93bChris Banes            width = Math.max(width, itemView.getMeasuredWidth());
573f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
574f933672168f6deff5e3636584bec735416b5a93bChris Banes
575f933672168f6deff5e3636584bec735416b5a93bChris Banes        // Add background padding to measured width
576f933672168f6deff5e3636584bec735416b5a93bChris Banes        if (background != null) {
577f933672168f6deff5e3636584bec735416b5a93bChris Banes            background.getPadding(mTempRect);
578f933672168f6deff5e3636584bec735416b5a93bChris Banes            width += mTempRect.left + mTempRect.right;
579f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
580f933672168f6deff5e3636584bec735416b5a93bChris Banes
581f933672168f6deff5e3636584bec735416b5a93bChris Banes        return width;
582f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
583f933672168f6deff5e3636584bec735416b5a93bChris Banes
584f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
585f933672168f6deff5e3636584bec735416b5a93bChris Banes     * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
586f933672168f6deff5e3636584bec735416b5a93bChris Banes     * into a ListAdapter.</p>
587f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
588f933672168f6deff5e3636584bec735416b5a93bChris Banes    private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
589f933672168f6deff5e3636584bec735416b5a93bChris Banes
590f933672168f6deff5e3636584bec735416b5a93bChris Banes        private SpinnerAdapter mAdapter;
591f933672168f6deff5e3636584bec735416b5a93bChris Banes
592f933672168f6deff5e3636584bec735416b5a93bChris Banes        private ListAdapter mListAdapter;
593f933672168f6deff5e3636584bec735416b5a93bChris Banes
594f933672168f6deff5e3636584bec735416b5a93bChris Banes        /**
595f933672168f6deff5e3636584bec735416b5a93bChris Banes         * Creates a new ListAdapter wrapper for the specified adapter.
596f933672168f6deff5e3636584bec735416b5a93bChris Banes         *
597f933672168f6deff5e3636584bec735416b5a93bChris Banes         * @param adapter       the SpinnerAdapter to transform into a ListAdapter
598f933672168f6deff5e3636584bec735416b5a93bChris Banes         * @param dropDownTheme the theme against which to inflate drop-down
599f933672168f6deff5e3636584bec735416b5a93bChris Banes         *                      views, may be {@null} to use default theme
600f933672168f6deff5e3636584bec735416b5a93bChris Banes         */
601f933672168f6deff5e3636584bec735416b5a93bChris Banes        public DropDownAdapter(@Nullable SpinnerAdapter adapter,
602f933672168f6deff5e3636584bec735416b5a93bChris Banes                @Nullable Resources.Theme dropDownTheme) {
603f933672168f6deff5e3636584bec735416b5a93bChris Banes            mAdapter = adapter;
604f933672168f6deff5e3636584bec735416b5a93bChris Banes
605f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (adapter instanceof ListAdapter) {
606f933672168f6deff5e3636584bec735416b5a93bChris Banes                mListAdapter = (ListAdapter) adapter;
607f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
608f933672168f6deff5e3636584bec735416b5a93bChris Banes
609f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (dropDownTheme != null) {
610f933672168f6deff5e3636584bec735416b5a93bChris Banes                 if (IS_AT_LEAST_M && adapter instanceof android.widget.ThemedSpinnerAdapter) {
611f933672168f6deff5e3636584bec735416b5a93bChris Banes                    final android.widget.ThemedSpinnerAdapter themedAdapter =
612f933672168f6deff5e3636584bec735416b5a93bChris Banes                            (android.widget.ThemedSpinnerAdapter) adapter;
613f933672168f6deff5e3636584bec735416b5a93bChris Banes                    if (themedAdapter.getDropDownViewTheme() != dropDownTheme) {
614f933672168f6deff5e3636584bec735416b5a93bChris Banes                        themedAdapter.setDropDownViewTheme(dropDownTheme);
615f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
616f933672168f6deff5e3636584bec735416b5a93bChris Banes                } else if (adapter instanceof ThemedSpinnerAdapter) {
617f933672168f6deff5e3636584bec735416b5a93bChris Banes                    final ThemedSpinnerAdapter themedAdapter = (ThemedSpinnerAdapter) adapter;
618f933672168f6deff5e3636584bec735416b5a93bChris Banes                    if (themedAdapter.getDropDownViewTheme() == null) {
619f933672168f6deff5e3636584bec735416b5a93bChris Banes                        themedAdapter.setDropDownViewTheme(dropDownTheme);
620f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
621f933672168f6deff5e3636584bec735416b5a93bChris Banes                }
622f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
623f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
624f933672168f6deff5e3636584bec735416b5a93bChris Banes
625f933672168f6deff5e3636584bec735416b5a93bChris Banes        public int getCount() {
626f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mAdapter == null ? 0 : mAdapter.getCount();
627f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
628f933672168f6deff5e3636584bec735416b5a93bChris Banes
629f933672168f6deff5e3636584bec735416b5a93bChris Banes        public Object getItem(int position) {
630f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mAdapter == null ? null : mAdapter.getItem(position);
631f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
632f933672168f6deff5e3636584bec735416b5a93bChris Banes
633f933672168f6deff5e3636584bec735416b5a93bChris Banes        public long getItemId(int position) {
634f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mAdapter == null ? -1 : mAdapter.getItemId(position);
635f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
636f933672168f6deff5e3636584bec735416b5a93bChris Banes
637f933672168f6deff5e3636584bec735416b5a93bChris Banes        public View getView(int position, View convertView, ViewGroup parent) {
638f933672168f6deff5e3636584bec735416b5a93bChris Banes            return getDropDownView(position, convertView, parent);
639f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
640f933672168f6deff5e3636584bec735416b5a93bChris Banes
641f933672168f6deff5e3636584bec735416b5a93bChris Banes        public View getDropDownView(int position, View convertView, ViewGroup parent) {
642f933672168f6deff5e3636584bec735416b5a93bChris Banes            return (mAdapter == null) ? null
643f933672168f6deff5e3636584bec735416b5a93bChris Banes                    : mAdapter.getDropDownView(position, convertView, parent);
644f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
645f933672168f6deff5e3636584bec735416b5a93bChris Banes
646f933672168f6deff5e3636584bec735416b5a93bChris Banes        public boolean hasStableIds() {
647f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mAdapter != null && mAdapter.hasStableIds();
648f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
649f933672168f6deff5e3636584bec735416b5a93bChris Banes
650f933672168f6deff5e3636584bec735416b5a93bChris Banes        public void registerDataSetObserver(DataSetObserver observer) {
651f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (mAdapter != null) {
652f933672168f6deff5e3636584bec735416b5a93bChris Banes                mAdapter.registerDataSetObserver(observer);
653f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
654f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
655f933672168f6deff5e3636584bec735416b5a93bChris Banes
656f933672168f6deff5e3636584bec735416b5a93bChris Banes        public void unregisterDataSetObserver(DataSetObserver observer) {
657f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (mAdapter != null) {
658f933672168f6deff5e3636584bec735416b5a93bChris Banes                mAdapter.unregisterDataSetObserver(observer);
659f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
660f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
661f933672168f6deff5e3636584bec735416b5a93bChris Banes
662f933672168f6deff5e3636584bec735416b5a93bChris Banes        /**
663f933672168f6deff5e3636584bec735416b5a93bChris Banes         * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
664f933672168f6deff5e3636584bec735416b5a93bChris Banes         * Otherwise, return true.
665f933672168f6deff5e3636584bec735416b5a93bChris Banes         */
666f933672168f6deff5e3636584bec735416b5a93bChris Banes        public boolean areAllItemsEnabled() {
667f933672168f6deff5e3636584bec735416b5a93bChris Banes            final ListAdapter adapter = mListAdapter;
668f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (adapter != null) {
669f933672168f6deff5e3636584bec735416b5a93bChris Banes                return adapter.areAllItemsEnabled();
670f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
671f933672168f6deff5e3636584bec735416b5a93bChris Banes                return true;
672f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
673f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
674f933672168f6deff5e3636584bec735416b5a93bChris Banes
675f933672168f6deff5e3636584bec735416b5a93bChris Banes        /**
676f933672168f6deff5e3636584bec735416b5a93bChris Banes         * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
677f933672168f6deff5e3636584bec735416b5a93bChris Banes         * Otherwise, return true.
678f933672168f6deff5e3636584bec735416b5a93bChris Banes         */
679f933672168f6deff5e3636584bec735416b5a93bChris Banes        public boolean isEnabled(int position) {
680f933672168f6deff5e3636584bec735416b5a93bChris Banes            final ListAdapter adapter = mListAdapter;
681f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (adapter != null) {
682f933672168f6deff5e3636584bec735416b5a93bChris Banes                return adapter.isEnabled(position);
683f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
684f933672168f6deff5e3636584bec735416b5a93bChris Banes                return true;
685f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
686f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
687f933672168f6deff5e3636584bec735416b5a93bChris Banes
688f933672168f6deff5e3636584bec735416b5a93bChris Banes        public int getItemViewType(int position) {
689f933672168f6deff5e3636584bec735416b5a93bChris Banes            return 0;
690f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
691f933672168f6deff5e3636584bec735416b5a93bChris Banes
692f933672168f6deff5e3636584bec735416b5a93bChris Banes        public int getViewTypeCount() {
693f933672168f6deff5e3636584bec735416b5a93bChris Banes            return 1;
694f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
695f933672168f6deff5e3636584bec735416b5a93bChris Banes
696f933672168f6deff5e3636584bec735416b5a93bChris Banes        public boolean isEmpty() {
697f933672168f6deff5e3636584bec735416b5a93bChris Banes            return getCount() == 0;
698f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
699f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
700f933672168f6deff5e3636584bec735416b5a93bChris Banes
701f933672168f6deff5e3636584bec735416b5a93bChris Banes    private class DropdownPopup extends ListPopupWindow {
702f933672168f6deff5e3636584bec735416b5a93bChris Banes        private CharSequence mHintText;
7032c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        ListAdapter mAdapter;
704f933672168f6deff5e3636584bec735416b5a93bChris Banes        private final Rect mVisibleRect = new Rect();
705f933672168f6deff5e3636584bec735416b5a93bChris Banes
706f933672168f6deff5e3636584bec735416b5a93bChris Banes        public DropdownPopup(Context context, AttributeSet attrs, int defStyleAttr) {
707f933672168f6deff5e3636584bec735416b5a93bChris Banes            super(context, attrs, defStyleAttr);
708f933672168f6deff5e3636584bec735416b5a93bChris Banes
709f933672168f6deff5e3636584bec735416b5a93bChris Banes            setAnchorView(AppCompatSpinner.this);
710f933672168f6deff5e3636584bec735416b5a93bChris Banes            setModal(true);
711f933672168f6deff5e3636584bec735416b5a93bChris Banes            setPromptPosition(POSITION_PROMPT_ABOVE);
712f933672168f6deff5e3636584bec735416b5a93bChris Banes
713f933672168f6deff5e3636584bec735416b5a93bChris Banes            setOnItemClickListener(new AdapterView.OnItemClickListener() {
714f933672168f6deff5e3636584bec735416b5a93bChris Banes                @Override
715f933672168f6deff5e3636584bec735416b5a93bChris Banes                public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
716f933672168f6deff5e3636584bec735416b5a93bChris Banes                    AppCompatSpinner.this.setSelection(position);
717f933672168f6deff5e3636584bec735416b5a93bChris Banes                    if (getOnItemClickListener() != null) {
718f933672168f6deff5e3636584bec735416b5a93bChris Banes                        AppCompatSpinner.this
719f933672168f6deff5e3636584bec735416b5a93bChris Banes                                .performItemClick(v, position, mAdapter.getItemId(position));
720f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
721f933672168f6deff5e3636584bec735416b5a93bChris Banes                    dismiss();
722f933672168f6deff5e3636584bec735416b5a93bChris Banes                }
723f933672168f6deff5e3636584bec735416b5a93bChris Banes            });
724f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
725f933672168f6deff5e3636584bec735416b5a93bChris Banes
726f933672168f6deff5e3636584bec735416b5a93bChris Banes        @Override
727f933672168f6deff5e3636584bec735416b5a93bChris Banes        public void setAdapter(ListAdapter adapter) {
728f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.setAdapter(adapter);
729f933672168f6deff5e3636584bec735416b5a93bChris Banes            mAdapter = adapter;
730f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
731f933672168f6deff5e3636584bec735416b5a93bChris Banes
732f933672168f6deff5e3636584bec735416b5a93bChris Banes        public CharSequence getHintText() {
733f933672168f6deff5e3636584bec735416b5a93bChris Banes            return mHintText;
734f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
735f933672168f6deff5e3636584bec735416b5a93bChris Banes
736f933672168f6deff5e3636584bec735416b5a93bChris Banes        public void setPromptText(CharSequence hintText) {
737f933672168f6deff5e3636584bec735416b5a93bChris Banes            // Hint text is ignored for dropdowns, but maintain it here.
738f933672168f6deff5e3636584bec735416b5a93bChris Banes            mHintText = hintText;
739f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
740f933672168f6deff5e3636584bec735416b5a93bChris Banes
741f933672168f6deff5e3636584bec735416b5a93bChris Banes        void computeContentWidth() {
742f933672168f6deff5e3636584bec735416b5a93bChris Banes            final Drawable background = getBackground();
743f933672168f6deff5e3636584bec735416b5a93bChris Banes            int hOffset = 0;
744f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (background != null) {
745f933672168f6deff5e3636584bec735416b5a93bChris Banes                background.getPadding(mTempRect);
746f933672168f6deff5e3636584bec735416b5a93bChris Banes                hOffset = ViewUtils.isLayoutRtl(AppCompatSpinner.this) ? mTempRect.right
747f933672168f6deff5e3636584bec735416b5a93bChris Banes                        : -mTempRect.left;
748f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
749f933672168f6deff5e3636584bec735416b5a93bChris Banes                mTempRect.left = mTempRect.right = 0;
750f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
751f933672168f6deff5e3636584bec735416b5a93bChris Banes
752f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int spinnerPaddingLeft = AppCompatSpinner.this.getPaddingLeft();
753f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int spinnerPaddingRight = AppCompatSpinner.this.getPaddingRight();
754f933672168f6deff5e3636584bec735416b5a93bChris Banes            final int spinnerWidth = AppCompatSpinner.this.getWidth();
755f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (mDropDownWidth == WRAP_CONTENT) {
756f933672168f6deff5e3636584bec735416b5a93bChris Banes                int contentWidth = compatMeasureContentWidth(
757f933672168f6deff5e3636584bec735416b5a93bChris Banes                        (SpinnerAdapter) mAdapter, getBackground());
758f933672168f6deff5e3636584bec735416b5a93bChris Banes                final int contentWidthLimit = getContext().getResources()
759f933672168f6deff5e3636584bec735416b5a93bChris Banes                        .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
760f933672168f6deff5e3636584bec735416b5a93bChris Banes                if (contentWidth > contentWidthLimit) {
761f933672168f6deff5e3636584bec735416b5a93bChris Banes                    contentWidth = contentWidthLimit;
762f933672168f6deff5e3636584bec735416b5a93bChris Banes                }
763f933672168f6deff5e3636584bec735416b5a93bChris Banes                setContentWidth(Math.max(
764f933672168f6deff5e3636584bec735416b5a93bChris Banes                        contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
765f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else if (mDropDownWidth == MATCH_PARENT) {
766f933672168f6deff5e3636584bec735416b5a93bChris Banes                setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
767f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
768f933672168f6deff5e3636584bec735416b5a93bChris Banes                setContentWidth(mDropDownWidth);
769f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
770f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (ViewUtils.isLayoutRtl(AppCompatSpinner.this)) {
771f933672168f6deff5e3636584bec735416b5a93bChris Banes                hOffset += spinnerWidth - spinnerPaddingRight - getWidth();
772f933672168f6deff5e3636584bec735416b5a93bChris Banes            } else {
773f933672168f6deff5e3636584bec735416b5a93bChris Banes                hOffset += spinnerPaddingLeft;
774f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
775f933672168f6deff5e3636584bec735416b5a93bChris Banes            setHorizontalOffset(hOffset);
776f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
777f933672168f6deff5e3636584bec735416b5a93bChris Banes
778f933672168f6deff5e3636584bec735416b5a93bChris Banes        public void show() {
779f933672168f6deff5e3636584bec735416b5a93bChris Banes            final boolean wasShowing = isShowing();
780f933672168f6deff5e3636584bec735416b5a93bChris Banes
781f933672168f6deff5e3636584bec735416b5a93bChris Banes            computeContentWidth();
782f933672168f6deff5e3636584bec735416b5a93bChris Banes
783f933672168f6deff5e3636584bec735416b5a93bChris Banes            setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
784f933672168f6deff5e3636584bec735416b5a93bChris Banes            super.show();
785f933672168f6deff5e3636584bec735416b5a93bChris Banes            final ListView listView = getListView();
786f933672168f6deff5e3636584bec735416b5a93bChris Banes            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
787f933672168f6deff5e3636584bec735416b5a93bChris Banes            setSelection(AppCompatSpinner.this.getSelectedItemPosition());
788f933672168f6deff5e3636584bec735416b5a93bChris Banes
789f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (wasShowing) {
790f933672168f6deff5e3636584bec735416b5a93bChris Banes                // Skip setting up the layout/dismiss listener below. If we were previously
791f933672168f6deff5e3636584bec735416b5a93bChris Banes                // showing it will still stick around.
792f933672168f6deff5e3636584bec735416b5a93bChris Banes                return;
793f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
794f933672168f6deff5e3636584bec735416b5a93bChris Banes
795f933672168f6deff5e3636584bec735416b5a93bChris Banes            // Make sure we hide if our anchor goes away.
796f933672168f6deff5e3636584bec735416b5a93bChris Banes            // TODO: This might be appropriate to push all the way down to PopupWindow,
797f933672168f6deff5e3636584bec735416b5a93bChris Banes            // but it may have other side effects to investigate first. (Text editing handles, etc.)
798f933672168f6deff5e3636584bec735416b5a93bChris Banes            final ViewTreeObserver vto = getViewTreeObserver();
799f933672168f6deff5e3636584bec735416b5a93bChris Banes            if (vto != null) {
800f933672168f6deff5e3636584bec735416b5a93bChris Banes                final ViewTreeObserver.OnGlobalLayoutListener layoutListener
801f933672168f6deff5e3636584bec735416b5a93bChris Banes                        = new ViewTreeObserver.OnGlobalLayoutListener() {
802f933672168f6deff5e3636584bec735416b5a93bChris Banes                    @Override
803f933672168f6deff5e3636584bec735416b5a93bChris Banes                    public void onGlobalLayout() {
804f933672168f6deff5e3636584bec735416b5a93bChris Banes                        if (!isVisibleToUser(AppCompatSpinner.this)) {
805f933672168f6deff5e3636584bec735416b5a93bChris Banes                            dismiss();
806f933672168f6deff5e3636584bec735416b5a93bChris Banes                        } else {
807f933672168f6deff5e3636584bec735416b5a93bChris Banes                            computeContentWidth();
808f933672168f6deff5e3636584bec735416b5a93bChris Banes
809f933672168f6deff5e3636584bec735416b5a93bChris Banes                            // Use super.show here to update; we don't want to move the selected
810f933672168f6deff5e3636584bec735416b5a93bChris Banes                            // position or adjust other things that would be reset otherwise.
811f933672168f6deff5e3636584bec735416b5a93bChris Banes                            DropdownPopup.super.show();
812f933672168f6deff5e3636584bec735416b5a93bChris Banes                        }
813f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
814f933672168f6deff5e3636584bec735416b5a93bChris Banes                };
815f933672168f6deff5e3636584bec735416b5a93bChris Banes                vto.addOnGlobalLayoutListener(layoutListener);
816f933672168f6deff5e3636584bec735416b5a93bChris Banes                setOnDismissListener(new PopupWindow.OnDismissListener() {
817f933672168f6deff5e3636584bec735416b5a93bChris Banes                    @Override
818f933672168f6deff5e3636584bec735416b5a93bChris Banes                    public void onDismiss() {
819f933672168f6deff5e3636584bec735416b5a93bChris Banes                        final ViewTreeObserver vto = getViewTreeObserver();
820f933672168f6deff5e3636584bec735416b5a93bChris Banes                        if (vto != null) {
821f933672168f6deff5e3636584bec735416b5a93bChris Banes                            vto.removeGlobalOnLayoutListener(layoutListener);
822f933672168f6deff5e3636584bec735416b5a93bChris Banes                        }
823f933672168f6deff5e3636584bec735416b5a93bChris Banes                    }
824f933672168f6deff5e3636584bec735416b5a93bChris Banes                });
825f933672168f6deff5e3636584bec735416b5a93bChris Banes            }
826f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
827f933672168f6deff5e3636584bec735416b5a93bChris Banes
828f933672168f6deff5e3636584bec735416b5a93bChris Banes        /**
829f933672168f6deff5e3636584bec735416b5a93bChris Banes         * Simplified version of the the hidden View.isVisibleToUser()
830f933672168f6deff5e3636584bec735416b5a93bChris Banes         */
8312c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        boolean isVisibleToUser(View view) {
832f933672168f6deff5e3636584bec735416b5a93bChris Banes            return ViewCompat.isAttachedToWindow(view) && view.getGlobalVisibleRect(mVisibleRect);
833f933672168f6deff5e3636584bec735416b5a93bChris Banes        }
834f933672168f6deff5e3636584bec735416b5a93bChris Banes    }
835415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes}
836