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
197b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikasimport android.content.Context;
209840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.ColorStateList;
2180de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banesimport android.content.res.Resources;
22097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.graphics.Color;
239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff;
246d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banesimport android.graphics.Rect;
259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable;
26097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.graphics.drawable.GradientDrawable;
270ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Weiimport android.support.annotation.Nullable;
28cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banesimport android.support.design.R;
297b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikasimport android.support.v4.content.ContextCompat;
3015d1695da89eed196884b44fe90f19f83fb37a4fChris Banesimport android.view.View;
31d9770e12c8ff2d4417700492c6616572be897e93Chris Banesimport android.view.ViewTreeObserver;
32f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banesimport android.view.animation.Interpolator;
339840efe3dbdc7026521da8576574c55120782f6cChris Banes
349840efe3dbdc7026521da8576574c55120782f6cChris Banesabstract class FloatingActionButtonImpl {
359840efe3dbdc7026521da8576574c55120782f6cChris Banes
36f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes    static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR;
37f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes    static final long PRESSED_ANIM_DURATION = 100;
38f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes    static final long PRESSED_ANIM_DELAY = 100;
39f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes
4015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    static final int ANIM_STATE_NONE = 0;
4115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    static final int ANIM_STATE_HIDING = 1;
4215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    static final int ANIM_STATE_SHOWING = 2;
4315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes
4415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    int mAnimState = ANIM_STATE_NONE;
4515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes
463d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    Drawable mShapeDrawable;
473d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    Drawable mRippleDrawable;
483d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    CircularBorderDrawable mBorderDrawable;
493d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    Drawable mContentBackground;
503d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
513d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    float mElevation;
523d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    float mPressedTranslationZ;
533d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
540ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    interface InternalVisibilityChangedListener {
550ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        public void onShown();
560ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei        public void onHidden();
570ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei    }
580ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei
59be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    static final int SHOW_HIDE_ANIM_DURATION = 200;
60be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
619840efe3dbdc7026521da8576574c55120782f6cChris Banes    static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
629840efe3dbdc7026521da8576574c55120782f6cChris Banes            android.R.attr.state_enabled};
639840efe3dbdc7026521da8576574c55120782f6cChris Banes    static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
649840efe3dbdc7026521da8576574c55120782f6cChris Banes            android.R.attr.state_enabled};
65f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes    static final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled};
669840efe3dbdc7026521da8576574c55120782f6cChris Banes    static final int[] EMPTY_STATE_SET = new int[0];
679840efe3dbdc7026521da8576574c55120782f6cChris Banes
68fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    final VisibilityAwareImageButton mView;
699840efe3dbdc7026521da8576574c55120782f6cChris Banes    final ShadowViewDelegate mShadowViewDelegate;
705a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes    final ValueAnimatorCompat.Creator mAnimatorCreator;
719840efe3dbdc7026521da8576574c55120782f6cChris Banes
726d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    private final Rect mTmpRect = new Rect();
73d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
74d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
75fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    FloatingActionButtonImpl(VisibilityAwareImageButton view,
765a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
779840efe3dbdc7026521da8576574c55120782f6cChris Banes        mView = view;
789840efe3dbdc7026521da8576574c55120782f6cChris Banes        mShadowViewDelegate = shadowViewDelegate;
795a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes        mAnimatorCreator = animatorCreator;
809840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
819840efe3dbdc7026521da8576574c55120782f6cChris Banes
82097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    abstract void setBackgroundDrawable(ColorStateList backgroundTint,
83cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes            PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth);
849840efe3dbdc7026521da8576574c55120782f6cChris Banes
859840efe3dbdc7026521da8576574c55120782f6cChris Banes    abstract void setBackgroundTintList(ColorStateList tint);
869840efe3dbdc7026521da8576574c55120782f6cChris Banes
879840efe3dbdc7026521da8576574c55120782f6cChris Banes    abstract void setBackgroundTintMode(PorterDuff.Mode tintMode);
889840efe3dbdc7026521da8576574c55120782f6cChris Banes
899840efe3dbdc7026521da8576574c55120782f6cChris Banes    abstract void setRippleColor(int rippleColor);
909840efe3dbdc7026521da8576574c55120782f6cChris Banes
913d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    final void setElevation(float elevation) {
923d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes        if (mElevation != elevation) {
933d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes            mElevation = elevation;
94f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes            onElevationsChanged(elevation, mPressedTranslationZ);
953d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes        }
963d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    }
973d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
986d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    abstract float getElevation();
996d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
1003d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    final void setPressedTranslationZ(float translationZ) {
1013d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes        if (mPressedTranslationZ != translationZ) {
1023d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes            mPressedTranslationZ = translationZ;
103f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes            onElevationsChanged(mElevation, translationZ);
1043d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes        }
1053d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    }
1063d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
107f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes    abstract void onElevationsChanged(float elevation, float pressedTranslationZ);
1089840efe3dbdc7026521da8576574c55120782f6cChris Banes
1099840efe3dbdc7026521da8576574c55120782f6cChris Banes    abstract void onDrawableStateChanged(int[] state);
1109840efe3dbdc7026521da8576574c55120782f6cChris Banes
1119840efe3dbdc7026521da8576574c55120782f6cChris Banes    abstract void jumpDrawableToCurrentState();
1129840efe3dbdc7026521da8576574c55120782f6cChris Banes
113fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    abstract void hide(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
114be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
115fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes    abstract void show(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
116be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
1173d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    final Drawable getContentBackground() {
1183d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes        return mContentBackground;
1193d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes    }
1203d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes
1216d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    abstract void onCompatShadowChanged();
1226d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
1236d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    final void updatePadding() {
1246d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        Rect rect = mTmpRect;
1256d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        getPadding(rect);
1266d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        onPaddingUpdated(rect);
1276d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes        mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
1286d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    }
1296d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
1306d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    abstract void getPadding(Rect rect);
1316d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
1326d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes    void onPaddingUpdated(Rect padding) {}
1336d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes
134d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    void onAttachedToWindow() {
135d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        if (requirePreDrawListener()) {
136d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            ensurePreDrawListener();
137d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            mView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
138d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        }
139d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
140d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
141d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    void onDetachedFromWindow() {
142d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        if (mPreDrawListener != null) {
143d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            mView.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
144d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            mPreDrawListener = null;
145d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        }
146d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
147d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
148d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    boolean requirePreDrawListener() {
149d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        return false;
150d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
151d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
152d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
1537b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas        final Context context = mView.getContext();
15480de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes        CircularBorderDrawable borderDrawable = newCircularDrawable();
15580de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes        borderDrawable.setGradientColors(
1567b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas                ContextCompat.getColor(context, R.color.design_fab_stroke_top_outer_color),
1577b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas                ContextCompat.getColor(context, R.color.design_fab_stroke_top_inner_color),
1587b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas                ContextCompat.getColor(context, R.color.design_fab_stroke_end_inner_color),
1597b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas                ContextCompat.getColor(context, R.color.design_fab_stroke_end_outer_color));
16080de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes        borderDrawable.setBorderWidth(borderWidth);
1618603357c9433b9aef1805413a3bfc4a4f8decc50Chris Banes        borderDrawable.setBorderTint(backgroundTint);
162092f4b4fc5610ed1a2c4f5108066726ee59d8f16Chris Banes        return borderDrawable;
16380de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes    }
16480de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes
16580de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes    CircularBorderDrawable newCircularDrawable() {
16680de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes        return new CircularBorderDrawable();
167cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes    }
168d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
169d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    void onPreDraw() {
170d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
171d9770e12c8ff2d4417700492c6616572be897e93Chris Banes
172d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    private void ensurePreDrawListener() {
173d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        if (mPreDrawListener == null) {
174d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
175d9770e12c8ff2d4417700492c6616572be897e93Chris Banes                @Override
176d9770e12c8ff2d4417700492c6616572be897e93Chris Banes                public boolean onPreDraw() {
177d9770e12c8ff2d4417700492c6616572be897e93Chris Banes                    FloatingActionButtonImpl.this.onPreDraw();
178d9770e12c8ff2d4417700492c6616572be897e93Chris Banes                    return true;
179d9770e12c8ff2d4417700492c6616572be897e93Chris Banes                }
180d9770e12c8ff2d4417700492c6616572be897e93Chris Banes            };
181d9770e12c8ff2d4417700492c6616572be897e93Chris Banes        }
182d9770e12c8ff2d4417700492c6616572be897e93Chris Banes    }
183097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes
184097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    GradientDrawable createShapeDrawable() {
185097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        GradientDrawable d = new GradientDrawable();
186097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        d.setShape(GradientDrawable.OVAL);
187097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        d.setColor(Color.WHITE);
188097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes        return d;
189097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes    }
19015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes
19115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    boolean isOrWillBeShown() {
19215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        if (mView.getVisibility() != View.VISIBLE) {
19315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            // If we not currently visible, return true if we're animating to be shown
19415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            return mAnimState == ANIM_STATE_SHOWING;
19515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        } else {
19615d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            // Otherwise if we're visible, return true if we're not animating to be hidden
19715d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            return mAnimState != ANIM_STATE_HIDING;
19815d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        }
19915d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    }
20015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes
20115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    boolean isOrWillBeHidden() {
20215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        if (mView.getVisibility() == View.VISIBLE) {
20315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            // If we currently visible, return true if we're animating to be hidden
20415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            return mAnimState == ANIM_STATE_HIDING;
20515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        } else {
20615d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            // Otherwise if we're not visible, return true if we're not animating to be shown
20715d1695da89eed196884b44fe90f19f83fb37a4fChris Banes            return mAnimState != ANIM_STATE_SHOWING;
20815d1695da89eed196884b44fe90f19f83fb37a4fChris Banes        }
20915d1695da89eed196884b44fe90f19f83fb37a4fChris Banes    }
2109840efe3dbdc7026521da8576574c55120782f6cChris Banes}
211