FloatingActionButton.java revision 1711e8729c1b901b73f530e87b7c9cc9370f33be
19840efe3dbdc7026521da8576574c55120782f6cChris Banes/*
29840efe3dbdc7026521da8576574c55120782f6cChris Banes * Copyright (C) 2015 The Android Open Source Project
39840efe3dbdc7026521da8576574c55120782f6cChris Banes *
49840efe3dbdc7026521da8576574c55120782f6cChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
59840efe3dbdc7026521da8576574c55120782f6cChris Banes * you may not use this file except in compliance with the License.
69840efe3dbdc7026521da8576574c55120782f6cChris Banes * You may obtain a copy of the License at
79840efe3dbdc7026521da8576574c55120782f6cChris Banes *
89840efe3dbdc7026521da8576574c55120782f6cChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
99840efe3dbdc7026521da8576574c55120782f6cChris Banes *
109840efe3dbdc7026521da8576574c55120782f6cChris Banes * Unless required by applicable law or agreed to in writing, software
119840efe3dbdc7026521da8576574c55120782f6cChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
129840efe3dbdc7026521da8576574c55120782f6cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139840efe3dbdc7026521da8576574c55120782f6cChris Banes * See the License for the specific language governing permissions and
149840efe3dbdc7026521da8576574c55120782f6cChris Banes * limitations under the License.
159840efe3dbdc7026521da8576574c55120782f6cChris Banes */
169840efe3dbdc7026521da8576574c55120782f6cChris Banes
179840efe3dbdc7026521da8576574c55120782f6cChris Banespackage android.support.design.widget;
189840efe3dbdc7026521da8576574c55120782f6cChris Banes
199840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.annotation.TargetApi;
209840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.Context;
219840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.ColorStateList;
229840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.TypedArray;
239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff;
249840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.Rect;
259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable;
269840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.os.Build;
277a13c8489daca7915623dd673df49de2d1a0bf30Chris Banesimport android.support.annotation.ColorInt;
281711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.annotation.DrawableRes;
29d9cbe69a6661315238d856abc22578d03666f63bChris Banesimport android.support.annotation.NonNull;
309840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.annotation.Nullable;
319840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.design.R;
320ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Weiimport android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener;
33b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat;
341711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.v7.widget.AppCompatDrawableManager;
351711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.v7.widget.AppCompatImageHelper;
369840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.util.AttributeSet;
37097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.util.Log;
3814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.View;
399840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.widget.ImageView;
409840efe3dbdc7026521da8576574c55120782f6cChris Banes
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
42b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
439840efe3dbdc7026521da8576574c55120782f6cChris Banes/**
449840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons are used for a special type of promoted action. They are distinguished
4514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * by a circled icon floating above the UI and have special motion behaviors related to morphing,
469840efe3dbdc7026521da8576574c55120782f6cChris Banes * launching, and the transferring anchor point.
479840efe3dbdc7026521da8576574c55120782f6cChris Banes *
489fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>Floating action buttons come in two sizes: the default and the mini. The size can be
499fb154338a62edc2c57dc036895199d6f1769400Chris Banes * controlled with the {@code fabSize} attribute.</p>
509fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
519fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed
529fb154338a62edc2c57dc036895199d6f1769400Chris Banes * via {@link #setImageDrawable(Drawable)}.</p>
539fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
549fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you
559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * wish to change this at runtime then you can do so via
569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * {@link #setBackgroundTintList(ColorStateList)}.</p>
579fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize
599840efe3dbdc7026521da8576574c55120782f6cChris Banes */
60b7f9224b1495db47eb8fd813b5912250e900770aChris Banes@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
61fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banespublic class FloatingActionButton extends VisibilityAwareImageButton {
629840efe3dbdc7026521da8576574c55120782f6cChris Banes
63097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    private static final String LOG_TAG = "FloatingActionButton";
64097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes
650ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    /**
660ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * Callback to be invoked when the visibility of a FloatingActionButton changes.
670ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     */
680ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    public abstract static class OnVisibilityChangedListener {
690ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        /**
700ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * Called when a FloatingActionButton has been
710ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * {@link #show(OnVisibilityChangedListener) shown}.
720ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         *
730ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * @param fab the FloatingActionButton that was shown.
740ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         */
750ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        public void onShown(FloatingActionButton fab) {}
760ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
770ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        /**
780ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * Called when a FloatingActionButton has been
790ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * {@link #hide(OnVisibilityChangedListener) hidden}.
800ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         *
810ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         * @param fab the FloatingActionButton that was hidden.
820ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei         */
830ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        public void onHidden(FloatingActionButton fab) {}
840ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    }
850ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
869840efe3dbdc7026521da8576574c55120782f6cChris Banes    // These values must match those in the attrs declaration
879840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static final int SIZE_MINI = 1;
889840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static final int SIZE_NORMAL = 0;
899840efe3dbdc7026521da8576574c55120782f6cChris Banes
909840efe3dbdc7026521da8576574c55120782f6cChris Banes    private ColorStateList mBackgroundTint;
919840efe3dbdc7026521da8576574c55120782f6cChris Banes    private PorterDuff.Mode mBackgroundTintMode;
929840efe3dbdc7026521da8576574c55120782f6cChris Banes
93cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes    private int mBorderWidth;
949840efe3dbdc7026521da8576574c55120782f6cChris Banes    private int mRippleColor;
959840efe3dbdc7026521da8576574c55120782f6cChris Banes    private int mSize;
96d9cbe69a6661315238d856abc22578d03666f63bChris Banes    private int mImagePadding;
979840efe3dbdc7026521da8576574c55120782f6cChris Banes
986d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    private boolean mCompatPadding;
99cdc736866534c604c4015c78371ade52bb6d52dfChris Banes    private final Rect mShadowPadding = new Rect();
1009840efe3dbdc7026521da8576574c55120782f6cChris Banes
1011711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes    private AppCompatImageHelper mImageHelper;
1021711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes
103d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes    private FloatingActionButtonImpl mImpl;
1049840efe3dbdc7026521da8576574c55120782f6cChris Banes
1059840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context) {
1069840efe3dbdc7026521da8576574c55120782f6cChris Banes        this(context, null);
1079840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1089840efe3dbdc7026521da8576574c55120782f6cChris Banes
1099840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context, AttributeSet attrs) {
1109840efe3dbdc7026521da8576574c55120782f6cChris Banes        this(context, attrs, 0);
1119840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1129840efe3dbdc7026521da8576574c55120782f6cChris Banes
1139840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
1149840efe3dbdc7026521da8576574c55120782f6cChris Banes        super(context, attrs, defStyleAttr);
1159840efe3dbdc7026521da8576574c55120782f6cChris Banes
116809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(context);
117809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
1189840efe3dbdc7026521da8576574c55120782f6cChris Banes        TypedArray a = context.obtainStyledAttributes(attrs,
1199840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton, defStyleAttr,
1209840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.style.Widget_Design_FloatingActionButton);
1219840efe3dbdc7026521da8576574c55120782f6cChris Banes        mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
1229840efe3dbdc7026521da8576574c55120782f6cChris Banes        mBackgroundTintMode = parseTintMode(a.getInt(
1239840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
1249840efe3dbdc7026521da8576574c55120782f6cChris Banes        mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
1259840efe3dbdc7026521da8576574c55120782f6cChris Banes        mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL);
126cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes        mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0);
1279840efe3dbdc7026521da8576574c55120782f6cChris Banes        final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f);
1289840efe3dbdc7026521da8576574c55120782f6cChris Banes        final float pressedTranslationZ = a.getDimension(
1299840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton_pressedTranslationZ, 0f);
1306d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        mCompatPadding = a.getBoolean(R.styleable.FloatingActionButton_useCompatPadding, false);
1319840efe3dbdc7026521da8576574c55120782f6cChris Banes        a.recycle();
1329840efe3dbdc7026521da8576574c55120782f6cChris Banes
1331711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes        mImageHelper = new AppCompatImageHelper(this, AppCompatDrawableManager.get());
1341711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes        mImageHelper.loadFromAttributes(attrs, defStyleAttr);
1351711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes
136d9cbe69a6661315238d856abc22578d03666f63bChris Banes        final int maxImageSize = (int) getResources().getDimension(R.dimen.design_fab_image_size);
137d9cbe69a6661315238d856abc22578d03666f63bChris Banes        mImagePadding = (getSizeDimension() - maxImageSize) / 2;
1389840efe3dbdc7026521da8576574c55120782f6cChris Banes
139d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode,
140097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes                mRippleColor, mBorderWidth);
141d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().setElevation(elevation);
142d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().setPressedTranslationZ(pressedTranslationZ);
143d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().updatePadding();
1449840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1459840efe3dbdc7026521da8576574c55120782f6cChris Banes
1469840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
1479840efe3dbdc7026521da8576574c55120782f6cChris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1489840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int preferredSize = getSizeDimension();
1499840efe3dbdc7026521da8576574c55120782f6cChris Banes
1509840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec);
1519840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec);
1529840efe3dbdc7026521da8576574c55120782f6cChris Banes
1539840efe3dbdc7026521da8576574c55120782f6cChris Banes        // As we want to stay circular, we set both dimensions to be the
1549840efe3dbdc7026521da8576574c55120782f6cChris Banes        // smallest resolved dimension
1559840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int d = Math.min(w, h);
1569840efe3dbdc7026521da8576574c55120782f6cChris Banes
1579840efe3dbdc7026521da8576574c55120782f6cChris Banes        // We add the shadow's padding to the measured dimension
1589840efe3dbdc7026521da8576574c55120782f6cChris Banes        setMeasuredDimension(
1599840efe3dbdc7026521da8576574c55120782f6cChris Banes                d + mShadowPadding.left + mShadowPadding.right,
1609840efe3dbdc7026521da8576574c55120782f6cChris Banes                d + mShadowPadding.top + mShadowPadding.bottom);
1619840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1629840efe3dbdc7026521da8576574c55120782f6cChris Banes
1639840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1649840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Set the ripple color for this {@link FloatingActionButton}.
1659840efe3dbdc7026521da8576574c55120782f6cChris Banes     * <p>
1669840efe3dbdc7026521da8576574c55120782f6cChris Banes     * When running on devices with KitKat or below, we draw a fill rather than a ripple.
1679840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1689840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param color ARGB color to use for the ripple.
1696d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
1706d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @attr ref android.support.design.R.styleable#FloatingActionButton_rippleColor
1719840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1727a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes    public void setRippleColor(@ColorInt int color) {
1739840efe3dbdc7026521da8576574c55120782f6cChris Banes        if (mRippleColor != color) {
1749840efe3dbdc7026521da8576574c55120782f6cChris Banes            mRippleColor = color;
175d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            getImpl().setRippleColor(color);
1769840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
1779840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1789840efe3dbdc7026521da8576574c55120782f6cChris Banes
1799840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1809840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Return the tint applied to the background drawable, if specified.
1819840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1829840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @return the tint applied to the background drawable
1839840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @see #setBackgroundTintList(ColorStateList)
1849840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1859840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Nullable
1869840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
1879840efe3dbdc7026521da8576574c55120782f6cChris Banes    public ColorStateList getBackgroundTintList() {
1889840efe3dbdc7026521da8576574c55120782f6cChris Banes        return mBackgroundTint;
1899840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1909840efe3dbdc7026521da8576574c55120782f6cChris Banes
1919840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1929840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Applies a tint to the background drawable. Does not modify the current tint
1939840efe3dbdc7026521da8576574c55120782f6cChris Banes     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
1949840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1959840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param tint the tint to apply, may be {@code null} to clear tint
1969840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1979840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setBackgroundTintList(@Nullable ColorStateList tint) {
1987a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes        if (mBackgroundTint != tint) {
1997a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes            mBackgroundTint = tint;
200d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            getImpl().setBackgroundTintList(tint);
2017a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes        }
2029840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2039840efe3dbdc7026521da8576574c55120782f6cChris Banes
2049840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
2059840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Return the blending mode used to apply the tint to the background
2069840efe3dbdc7026521da8576574c55120782f6cChris Banes     * drawable, if specified.
2079840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
2089840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @return the blending mode used to apply the tint to the background
2099840efe3dbdc7026521da8576574c55120782f6cChris Banes     *         drawable
2109840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @see #setBackgroundTintMode(PorterDuff.Mode)
2119840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
2129840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Nullable
2139840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
2149840efe3dbdc7026521da8576574c55120782f6cChris Banes    public PorterDuff.Mode getBackgroundTintMode() {
2159840efe3dbdc7026521da8576574c55120782f6cChris Banes        return mBackgroundTintMode;
2169840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2179840efe3dbdc7026521da8576574c55120782f6cChris Banes
2189840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
2199840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Specifies the blending mode used to apply the tint specified by
2209840efe3dbdc7026521da8576574c55120782f6cChris Banes     * {@link #setBackgroundTintList(ColorStateList)}} to the background
2219840efe3dbdc7026521da8576574c55120782f6cChris Banes     * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
2229840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
2239840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param tintMode the blending mode used to apply the tint, may be
2249840efe3dbdc7026521da8576574c55120782f6cChris Banes     *                 {@code null} to clear tint
2259840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
2269840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
2277a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes        if (mBackgroundTintMode != tintMode) {
2287a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes            mBackgroundTintMode = tintMode;
229d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            getImpl().setBackgroundTintMode(tintMode);
2307a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes        }
2319840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2329840efe3dbdc7026521da8576574c55120782f6cChris Banes
2339840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
234097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    public void setBackgroundDrawable(Drawable background) {
235097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        Log.i(LOG_TAG, "Setting a custom background is not supported.");
236097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    }
237097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes
238097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    @Override
239097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    public void setBackgroundResource(int resid) {
240097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        Log.i(LOG_TAG, "Setting a custom background is not supported.");
241097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    }
242097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes
243097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    @Override
244097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    public void setBackgroundColor(int color) {
245097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        Log.i(LOG_TAG, "Setting a custom background is not supported.");
246be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    }
247be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
2481711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes    @Override
2491711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes    public void setImageResource(@DrawableRes int resId) {
2501711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes        // Intercept this call and instead retrieve the Drawable via the image helper
2511711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes        mImageHelper.setImageResource(resId);
2521711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes    }
2531711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes
254be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    /**
255be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * Shows the button.
2568c05e5f52fbc790b745e768398d9e69d6b9d9ee1Chris Banes     * <p>This method will animate the button show if the view has already been laid out.</p>
257be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     */
258be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    public void show() {
259fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes        show(null);
2600ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    }
2610ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
2620ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    /**
2630ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * Shows the button.
2648c05e5f52fbc790b745e768398d9e69d6b9d9ee1Chris Banes     * <p>This method will animate the button show if the view has already been laid out.</p>
2650ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     *
2660ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * @param listener the listener to notify when this view is shown
2670ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     */
2680ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    public void show(@Nullable final OnVisibilityChangedListener listener) {
269fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes        show(listener, true);
270fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    }
271fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes
272fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    private void show(OnVisibilityChangedListener listener, boolean fromUser) {
273d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().show(wrapOnVisibilityChangedListener(listener), fromUser);
274be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    }
275be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
276be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    /**
277be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * Hides the button.
278be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * <p>This method will animate the button hide if the view has already been laid out.</p>
279be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     */
280be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    public void hide() {
281fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes        hide(null);
2820ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    }
2830ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
2840ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    /**
2850ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * Hides the button.
2860ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * <p>This method will animate the button hide if the view has already been laid out.</p>
2870ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     *
2880ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     * @param listener the listener to notify when this view is hidden
2890ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei     */
2900ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    public void hide(@Nullable OnVisibilityChangedListener listener) {
291fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes        hide(listener, true);
292fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    }
293fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes
294fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    private void hide(@Nullable OnVisibilityChangedListener listener, boolean fromUser) {
295d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().hide(wrapOnVisibilityChangedListener(listener), fromUser);
2960ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    }
2970ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
2986d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    /**
2996d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * Set whether FloatingActionButton should add inner padding on platforms Lollipop and after,
3006d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * to ensure consistent dimensions on all platforms.
3016d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
3026d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @param useCompatPadding true if FloatingActionButton is adding inner padding on platforms
3036d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *                         Lollipop and after, to ensure consistent dimensions on all platforms.
3046d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
3056d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
3066d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @see #getUseCompatPadding()
3076d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     */
3086d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    public void setUseCompatPadding(boolean useCompatPadding) {
3096d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        if (mCompatPadding != useCompatPadding) {
3106d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes            mCompatPadding = useCompatPadding;
311d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            getImpl().onCompatShadowChanged();
3126d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        }
3136d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    }
3146d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
3156d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    /**
3166d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * Returns whether FloatingActionButton will add inner padding on platforms Lollipop and after.
3176d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
3186d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @return true if FloatingActionButton is adding inner padding on platforms Lollipop and after,
3196d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * to ensure consistent dimensions on all platforms.
3206d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
3216d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding
3226d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @see #setUseCompatPadding(boolean)
3236d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     */
3246d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    public boolean getUseCompatPadding() {
3256d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        return mCompatPadding;
3266d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    }
3276d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
3280ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    @Nullable
3290ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    private InternalVisibilityChangedListener wrapOnVisibilityChangedListener(
3300ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            @Nullable final OnVisibilityChangedListener listener) {
3310ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        if (listener == null) {
3320ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            return null;
3330ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        }
3340ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
3350ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        return new InternalVisibilityChangedListener() {
3360ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            @Override
3370ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            public void onShown() {
3380ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei                listener.onShown(FloatingActionButton.this);
3390ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            }
3400ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
3410ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            @Override
3420ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            public void onHidden() {
3430ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei                listener.onHidden(FloatingActionButton.this);
3440ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei            }
3450ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        };
3469840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
3479840efe3dbdc7026521da8576574c55120782f6cChris Banes
3489840efe3dbdc7026521da8576574c55120782f6cChris Banes    final int getSizeDimension() {
3499840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (mSize) {
3509840efe3dbdc7026521da8576574c55120782f6cChris Banes            case SIZE_MINI:
351a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                return getResources().getDimensionPixelSize(R.dimen.design_fab_size_mini);
3529840efe3dbdc7026521da8576574c55120782f6cChris Banes            case SIZE_NORMAL:
3539840efe3dbdc7026521da8576574c55120782f6cChris Banes            default:
354a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                return getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal);
3559840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
3569840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
3579840efe3dbdc7026521da8576574c55120782f6cChris Banes
3589840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
359d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    protected void onAttachedToWindow() {
360d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        super.onAttachedToWindow();
361d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().onAttachedToWindow();
362d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
363d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
364d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    @Override
365d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    protected void onDetachedFromWindow() {
366d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        super.onDetachedFromWindow();
367d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().onDetachedFromWindow();
368d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
369d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
370d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    @Override
3719840efe3dbdc7026521da8576574c55120782f6cChris Banes    protected void drawableStateChanged() {
3729840efe3dbdc7026521da8576574c55120782f6cChris Banes        super.drawableStateChanged();
373d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().onDrawableStateChanged(getDrawableState());
3749840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
3759840efe3dbdc7026521da8576574c55120782f6cChris Banes
3769840efe3dbdc7026521da8576574c55120782f6cChris Banes    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
3779840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
3789840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void jumpDrawablesToCurrentState() {
3799840efe3dbdc7026521da8576574c55120782f6cChris Banes        super.jumpDrawablesToCurrentState();
380d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().jumpDrawableToCurrentState();
3819840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
3829840efe3dbdc7026521da8576574c55120782f6cChris Banes
383d9cbe69a6661315238d856abc22578d03666f63bChris Banes    /**
384d9cbe69a6661315238d856abc22578d03666f63bChris Banes     * Return in {@code rect} the bounds of the actual floating action button content in view-local
385d9cbe69a6661315238d856abc22578d03666f63bChris Banes     * coordinates. This is defined as anything within any visible shadow.
386d9cbe69a6661315238d856abc22578d03666f63bChris Banes     *
387d9cbe69a6661315238d856abc22578d03666f63bChris Banes     * @return true if this view actually has been laid out and has a content rect, else false.
388d9cbe69a6661315238d856abc22578d03666f63bChris Banes     */
389d9cbe69a6661315238d856abc22578d03666f63bChris Banes    public boolean getContentRect(@NonNull Rect rect) {
390d9cbe69a6661315238d856abc22578d03666f63bChris Banes        if (ViewCompat.isLaidOut(this)) {
391d9cbe69a6661315238d856abc22578d03666f63bChris Banes            rect.set(0, 0, getWidth(), getHeight());
392d9cbe69a6661315238d856abc22578d03666f63bChris Banes            rect.left += mShadowPadding.left;
393d9cbe69a6661315238d856abc22578d03666f63bChris Banes            rect.top += mShadowPadding.top;
394d9cbe69a6661315238d856abc22578d03666f63bChris Banes            rect.right -= mShadowPadding.right;
395d9cbe69a6661315238d856abc22578d03666f63bChris Banes            rect.bottom -= mShadowPadding.bottom;
396d9cbe69a6661315238d856abc22578d03666f63bChris Banes            return true;
397d9cbe69a6661315238d856abc22578d03666f63bChris Banes        } else {
398d9cbe69a6661315238d856abc22578d03666f63bChris Banes            return false;
399d9cbe69a6661315238d856abc22578d03666f63bChris Banes        }
400d9cbe69a6661315238d856abc22578d03666f63bChris Banes    }
401d9cbe69a6661315238d856abc22578d03666f63bChris Banes
4023d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    /**
4033d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes     * Returns the FloatingActionButton's background, minus any compatible shadow implementation.
4043d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes     */
4053d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    @NonNull
4063d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    public Drawable getContentBackground() {
407d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        return getImpl().getContentBackground();
4083d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    }
4093d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
4109840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static int resolveAdjustedSize(int desiredSize, int measureSpec) {
4119840efe3dbdc7026521da8576574c55120782f6cChris Banes        int result = desiredSize;
4129840efe3dbdc7026521da8576574c55120782f6cChris Banes        int specMode = MeasureSpec.getMode(measureSpec);
4139840efe3dbdc7026521da8576574c55120782f6cChris Banes        int specSize = MeasureSpec.getSize(measureSpec);
4149840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (specMode) {
4159840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.UNSPECIFIED:
4169840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Parent says we can be as big as we want. Just don't be larger
4179840efe3dbdc7026521da8576574c55120782f6cChris Banes                // than max size imposed on ourselves.
4189840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = desiredSize;
4199840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
4209840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.AT_MOST:
4219840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Parent says we can be as big as we want, up to specSize.
4229840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Don't be larger than specSize, and don't be larger than
4239840efe3dbdc7026521da8576574c55120782f6cChris Banes                // the max size imposed on ourselves.
4249840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = Math.min(desiredSize, specSize);
4259840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
4269840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.EXACTLY:
4279840efe3dbdc7026521da8576574c55120782f6cChris Banes                // No choice. Do what we are told.
4289840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = specSize;
4299840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
4309840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
4319840efe3dbdc7026521da8576574c55120782f6cChris Banes        return result;
4329840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
4339840efe3dbdc7026521da8576574c55120782f6cChris Banes
4349840efe3dbdc7026521da8576574c55120782f6cChris Banes    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
4359840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (value) {
4369840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 3:
4379840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_OVER;
4389840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 5:
4399840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_IN;
4409840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 9:
4419840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_ATOP;
4429840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 14:
4439840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.MULTIPLY;
4449840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 15:
4459840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SCREEN;
4469840efe3dbdc7026521da8576574c55120782f6cChris Banes            default:
4479840efe3dbdc7026521da8576574c55120782f6cChris Banes                return defaultMode;
4489840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
4499840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
45014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes
45114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes    /**
452b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Behavior designed for use with {@link FloatingActionButton} instances. It's main function
453b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do
454b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * not cover them.
455b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
456b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
457b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is
458b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        // because we can use view translation properties which greatly simplifies the code.
459b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
460b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
461a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes        private ValueAnimatorCompat mFabTranslationYAnimator;
46218d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes        private float mFabTranslationY;
463a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private Rect mTmpRect;
464b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
465b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
466b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean layoutDependsOn(CoordinatorLayout parent,
467be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                FloatingActionButton child, View dependency) {
468a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We're dependent on all SnackbarLayouts (if enabled)
469a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
470b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
471b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
472b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
473b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
474a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
475a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (dependency instanceof Snackbar.SnackbarLayout) {
476a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                updateFabTranslationForSnackbar(parent, child, dependency);
477a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (dependency instanceof AppBarLayout) {
478be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // If we're depending on an AppBarLayout we will show/hide it automatically
479be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // if the FAB is anchored to the AppBarLayout
480be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                updateFabVisibility(parent, (AppBarLayout) dependency, child);
481be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
482be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return false;
483be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
484a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
485be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        private boolean updateFabVisibility(CoordinatorLayout parent,
486be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                AppBarLayout appBarLayout, FloatingActionButton child) {
487be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final CoordinatorLayout.LayoutParams lp =
488be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();
489be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (lp.getAnchorId() != appBarLayout.getId()) {
490be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // The anchor ID doesn't match the dependency, so we won't automatically
491be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // show/hide the FAB
492be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                return false;
493be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
494a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
495fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes            if (child.getUserSetVisibility() != VISIBLE) {
496fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes                // The view isn't set to be visible so skip changing it's visibility
497fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes                return false;
498fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes            }
499fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes
500be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (mTmpRect == null) {
501be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                mTmpRect = new Rect();
502a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
503be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
504be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // First, let's get the visible rect of the dependency
505be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final Rect rect = mTmpRect;
506be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
507be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
508be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
509be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // If the anchor's bottom is below the seam, we'll animate our FAB out
510fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes                child.hide(null, false);
511be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            } else {
512be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // Else, we'll animate our FAB back in
513fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes                child.show(null, false);
514be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
515be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return true;
516b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
517b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
518a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
519a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                final FloatingActionButton fab, View snackbar) {
52018d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            final float targetTransY = getFabTranslationYForSnackbar(parent, fab);
52118d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            if (mFabTranslationY == targetTransY) {
52218d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                // We're already at (or currently animating to) the target value, return...
52318d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                return;
52418d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            }
52518d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes
52618d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            final float currentTransY = ViewCompat.getTranslationY(fab);
52718d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes
528a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes            // Make sure that any current animation is cancelled
529a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes            if (mFabTranslationYAnimator != null && mFabTranslationYAnimator.isRunning()) {
530a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                mFabTranslationYAnimator.cancel();
531a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes            }
532a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes
533e33473d0e35315c02243363a7479a2c361765751Chris Banes            if (fab.isShown()
534e33473d0e35315c02243363a7479a2c361765751Chris Banes                    && Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
53518d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                // If the FAB will be travelling by more than 2/3 of it's height, let's animate
53618d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                // it instead
537a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                if (mFabTranslationYAnimator == null) {
538a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                    mFabTranslationYAnimator = ViewUtils.createAnimator();
539a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                    mFabTranslationYAnimator.setInterpolator(
540a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                            AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
541a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                    mFabTranslationYAnimator.setUpdateListener(
542a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                            new ValueAnimatorCompat.AnimatorUpdateListener() {
543a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                                @Override
544a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                                public void onAnimationUpdate(ValueAnimatorCompat animator) {
545a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                                    ViewCompat.setTranslationY(fab,
546a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                                            animator.getAnimatedFloatValue());
547a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                                }
548a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                            });
549a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                }
550a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                mFabTranslationYAnimator.setFloatValues(currentTransY, targetTransY);
551a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes                mFabTranslationYAnimator.start();
55218d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            } else {
55318d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                // Now update the translation Y
55418d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes                ViewCompat.setTranslationY(fab, targetTransY);
55518d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes            }
556a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes
557a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes            mFabTranslationY = targetTransY;
558b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
559b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private float getFabTranslationYForSnackbar(CoordinatorLayout parent,
561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                FloatingActionButton fab) {
562b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            float minOffset = 0;
563a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final List<View> dependencies = parent.getDependencies(fab);
564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = dependencies.size(); i < z; i++) {
565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View view = dependencies.get(i);
566a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
567a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    minOffset = Math.min(minOffset,
568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            ViewCompat.getTranslationY(view) - view.getHeight());
569b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
570b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
572b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return minOffset;
573b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
574b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
575e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        @Override
576e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
577e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                int layoutDirection) {
578be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // First, lets make sure that the visibility of the FAB is consistent
579be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final List<View> dependencies = parent.getDependencies(child);
580be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            for (int i = 0, count = dependencies.size(); i < count; i++) {
581be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                final View dependency = dependencies.get(i);
582be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                if (dependency instanceof AppBarLayout
583be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                        && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
584be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                    break;
585be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                }
586be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
587be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // Now let the CoordinatorLayout lay out the FAB
588e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            parent.onLayoutChild(child, layoutDirection);
589e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            // Now offset it if needed
590e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            offsetIfNeeded(parent, child);
591e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            return true;
592e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        }
593e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
594e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        /**
595e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
596e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * offsets our layout position so that we're positioned correctly if we're on one of
597e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * our parent's edges.
598e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         */
599e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) {
600e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            final Rect padding = fab.mShadowPadding;
601e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
602e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) {
603e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                final CoordinatorLayout.LayoutParams lp =
604e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                        (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
605e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
606e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                int offsetTB = 0, offsetLR = 0;
607e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
608e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                if (fab.getRight() >= parent.getWidth() - lp.rightMargin) {
609e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the left edge, shift it the right
610e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetLR = padding.right;
611e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                } else if (fab.getLeft() <= lp.leftMargin) {
612e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the left edge, shift it the left
613e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetLR = -padding.left;
614e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                }
615e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                if (fab.getBottom() >= parent.getBottom() - lp.bottomMargin) {
616e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the bottom edge, shift it down
617e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetTB = padding.bottom;
618e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                } else if (fab.getTop() <= lp.topMargin) {
619e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the top edge, shift it up
620e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetTB = -padding.top;
621e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                }
622e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
623e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                fab.offsetTopAndBottom(offsetTB);
624e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                fab.offsetLeftAndRight(offsetLR);
625e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            }
626e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        }
627b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
6286d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
6296d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    /**
6306d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * Returns the backward compatible elevation of the FloatingActionButton.
6316d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
6322b1d1d93a93070601d3894f523d6421c64544246Chris Banes     * @return the backward compatible elevation in pixels.
6336d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
6341e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes     * @see #setCompatElevation(float)
6356d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     */
6361e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes    public float getCompatElevation() {
637d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        return getImpl().getElevation();
6386d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    }
6396d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
6406d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    /**
6416d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * Updates the backward compatible elevation of the FloatingActionButton.
6426d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     *
6436d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @param elevation The backward compatible elevation in pixels.
6446d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation
6451e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes     * @see #getCompatElevation()
6466d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     * @see #setUseCompatPadding(boolean)
6476d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes     */
6481e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes    public void setCompatElevation(float elevation) {
649d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        getImpl().setElevation(elevation);
6506d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    }
651cdc736866534c604c4015c78371ade52bb6d52dfChris Banes
652d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes    private FloatingActionButtonImpl getImpl() {
653d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        if (mImpl == null) {
654d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            mImpl = createImpl();
655d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        }
656d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes        return mImpl;
657d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes    }
658d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes
659d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes    private FloatingActionButtonImpl createImpl() {
660cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        final int sdk = Build.VERSION.SDK_INT;
661cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        if (sdk >= 21) {
662d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl());
663cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        } else if (sdk >= 14) {
664d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            return new FloatingActionButtonIcs(this, new ShadowDelegateImpl());
665cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        } else {
666d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes            return new FloatingActionButtonEclairMr1(this, new ShadowDelegateImpl());
667cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        }
668cdc736866534c604c4015c78371ade52bb6d52dfChris Banes    }
669cdc736866534c604c4015c78371ade52bb6d52dfChris Banes
670cdc736866534c604c4015c78371ade52bb6d52dfChris Banes    private class ShadowDelegateImpl implements ShadowViewDelegate {
671cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        @Override
672cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        public float getRadius() {
673cdc736866534c604c4015c78371ade52bb6d52dfChris Banes            return getSizeDimension() / 2f;
674cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        }
675cdc736866534c604c4015c78371ade52bb6d52dfChris Banes
676cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        @Override
677cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        public void setShadowPadding(int left, int top, int right, int bottom) {
678cdc736866534c604c4015c78371ade52bb6d52dfChris Banes            mShadowPadding.set(left, top, right, bottom);
679cdc736866534c604c4015c78371ade52bb6d52dfChris Banes            setPadding(left + mImagePadding, top + mImagePadding,
680cdc736866534c604c4015c78371ade52bb6d52dfChris Banes                    right + mImagePadding, bottom + mImagePadding);
681cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        }
682cdc736866534c604c4015c78371ade52bb6d52dfChris Banes
683cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        @Override
684cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        public void setBackgroundDrawable(Drawable background) {
685cdc736866534c604c4015c78371ade52bb6d52dfChris Banes            FloatingActionButton.super.setBackgroundDrawable(background);
686cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        }
687cdc736866534c604c4015c78371ade52bb6d52dfChris Banes
688cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        @Override
689cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        public boolean isCompatPaddingEnabled() {
690cdc736866534c604c4015c78371ade52bb6d52dfChris Banes            return mCompatPadding;
691cdc736866534c604c4015c78371ade52bb6d52dfChris Banes        }
692cdc736866534c604c4015c78371ade52bb6d52dfChris Banes    }
6939840efe3dbdc7026521da8576574c55120782f6cChris Banes}
694