Snackbar.java revision df00f11c8c90db7b26672267fd1b74250c0d6cef
1b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/*
2b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Copyright (C) 2015 The Android Open Source Project
3b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
4b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * you may not use this file except in compliance with the License.
6b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * You may obtain a copy of the License at
7b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
8b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9b7f9224b1495db47eb8fd813b5912250e900770aChris Banes *
10b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Unless required by applicable law or agreed to in writing, software
11b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * See the License for the specific language governing permissions and
14b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * limitations under the License.
15b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */
16b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
17b7f9224b1495db47eb8fd813b5912250e900770aChris Banespackage android.support.design.widget;
18b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
19b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.content.Context;
204112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banesimport android.content.res.ColorStateList;
21b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.content.res.TypedArray;
22b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.os.Build;
23b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.os.Handler;
24b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.os.Looper;
25b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.os.Message;
265bb1f5d910c81ed2a31c687583bd90baccc972acChris Banesimport android.support.annotation.ColorInt;
27b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.annotation.IntDef;
285bb1f5d910c81ed2a31c687583bd90baccc972acChris Banesimport android.support.annotation.NonNull;
29b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.annotation.StringRes;
30b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.design.R;
31b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat;
32b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
33b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.text.TextUtils;
34b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.util.AttributeSet;
35b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.LayoutInflater;
36b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.MotionEvent;
37b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.View;
38b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.ViewGroup;
39c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banesimport android.view.ViewParent;
40b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.animation.Animation;
41b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.animation.AnimationUtils;
425cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banesimport android.widget.Button;
43c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banesimport android.widget.FrameLayout;
44b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.widget.LinearLayout;
45b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.widget.TextView;
46b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
47b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.Retention;
48b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.RetentionPolicy;
49b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
50b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
51b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
52b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/**
53b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Snackbars provide lightweight feedback about an operation. They show a brief message at the
54b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
55b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * elements on screen and only one can be displayed at a time.
56b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * <p>
57b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * They automatically disappear after a timeout or after user interaction elsewhere on the screen,
58b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * particularly after interactions that summon a new surface or activity. Snackbars can be swiped
59b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * off screen.
60b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * <p>
61b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Snackbars can contain an action which is set via
62b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
63bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * <p>
64bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
65bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * via {@link #setCallback(Callback)}.</p>
66b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */
67e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banespublic final class Snackbar {
68b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
69b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
70bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * Callback class for {@link Snackbar} instances.
71bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     *
72bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * @see Snackbar#setCallback(Callback)
73bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     */
74bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    public static abstract class Callback {
75e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a swipe.*/
76e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_SWIPE = 0;
77e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via an action click.*/
78e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_ACTION = 1;
79e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a timeout.*/
80e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_TIMEOUT = 2;
81e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
82e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_MANUAL = 3;
83e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
84e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_CONSECUTIVE = 4;
85e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
86e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** @hide */
87e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        @IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
88e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
89e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        @Retention(RetentionPolicy.SOURCE)
90e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public @interface DismissEvent {}
91e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
92bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        /**
93bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * Called when the given {@link Snackbar} has been dismissed, either through a time-out,
94bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * having been manually dismissed, or an action being clicked.
95bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         *
96bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @param snackbar The snackbar which has been dismissed.
97e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         * @param event The event which caused the dismissal. One of either:
98e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
99e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
100e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_CONSECUTIVE}.
101e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *
102bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @see Snackbar#dismiss()
103bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         */
104e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public void onDismissed(Snackbar snackbar, @DismissEvent int event) {
105bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes            // empty
106bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
107bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
108bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        /**
109bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * Called when the given {@link Snackbar} is visible.
110bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         *
111bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @param snackbar The snackbar which is now visible.
112bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @see Snackbar#show()
113bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         */
114bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        public void onShown(Snackbar snackbar) {
115bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes            // empty
116bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
117bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    }
118bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
119bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    /**
120b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @hide
121b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
1220bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
123b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Retention(RetentionPolicy.SOURCE)
124b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public @interface Duration {}
125b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
126b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
1270bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
1280bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
1290bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     *
1300bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * @see #setDuration
1310bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     */
1320bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    public static final int LENGTH_INDEFINITE = -2;
1330bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes
1340bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    /**
135b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the Snackbar for a short period of time.
136b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
137b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
138b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
139b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int LENGTH_SHORT = -1;
140b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
141b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
142b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the Snackbar for a long period of time.
143b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
144b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
145b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
146b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int LENGTH_LONG = 0;
147b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
148df00f11c8c90db7b26672267fd1b74250c0d6cefChris Banes    static final int ANIMATION_DURATION = 250;
149df00f11c8c90db7b26672267fd1b74250c0d6cefChris Banes    static final int ANIMATION_FADE_DURATION = 180;
150b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
151b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final Handler sHandler;
152b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final int MSG_SHOW = 0;
153b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private static final int MSG_DISMISS = 1;
154b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
155b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    static {
156b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
157b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            @Override
158b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            public boolean handleMessage(Message message) {
159b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                switch (message.what) {
160b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MSG_SHOW:
161b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ((Snackbar) message.obj).showView();
162b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        return true;
163b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MSG_DISMISS:
164e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                        ((Snackbar) message.obj).hideView(message.arg1);
165b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        return true;
166b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
167b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                return false;
168b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
169b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        });
170b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
171b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
1725462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    private final ViewGroup mTargetParent;
173b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private final Context mContext;
174b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private final SnackbarLayout mView;
175b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private int mDuration;
176bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    private Callback mCallback;
177b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
178e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private Snackbar(ViewGroup parent) {
1795462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mTargetParent = parent;
180b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mContext = parent.getContext();
181b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
182809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(mContext);
183809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
184b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        LayoutInflater inflater = LayoutInflater.from(mContext);
1855462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mView = (SnackbarLayout) inflater.inflate(
1865462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                R.layout.design_layout_snackbar, mTargetParent, false);
187b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
188b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
189b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
190c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * Make a Snackbar to display a message
191c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
192c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
193c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
194c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
195c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * whichever comes first.
196c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
197c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
198c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * certain features, such as swipe-to-dismiss and automatically moving of widgets like
199c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * {@link FloatingActionButton}.
200b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
201c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param view     The view to find a parent from.
202b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param text     The text to show.  Can be formatted text.
203b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
204b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 #LENGTH_LONG}
205b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
206e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
2075bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
2085bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes            @Duration int duration) {
209c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        Snackbar snackbar = new Snackbar(findSuitableParent(view));
210b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        snackbar.setText(text);
211b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        snackbar.setDuration(duration);
212b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return snackbar;
213b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
214b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
215b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
216b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Make a Snackbar to display a message.
217b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
218c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
219c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
220c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
221c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * whichever comes first.
222c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
223c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
224c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * certain features, such as swipe-to-dismiss and automatically moving of widgets like
225c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * {@link FloatingActionButton}.
226c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
227c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param view     The view to find a parent from.
228c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param resId    The resource id of the string resource to use. Can be formatted text.
229b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
230b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 #LENGTH_LONG}
231b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
232e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
2335bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
234c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        return make(view, view.getResources().getText(resId), duration);
235c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes    }
236c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
237c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes    private static ViewGroup findSuitableParent(View view) {
238c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        ViewGroup fallback = null;
239c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        do {
240c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            if (view instanceof CoordinatorLayout) {
241c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                // We've found a CoordinatorLayout, use it
242c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                return (ViewGroup) view;
243c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            } else if (view instanceof FrameLayout) {
244c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                if (view.getId() == android.R.id.content) {
245c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // If we've hit the decor content view, then we didn't find a CoL in the
246c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // hierarchy, so use it.
247c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    return (ViewGroup) view;
248c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                } else {
249c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // It's not the content view but we'll use it as our fallback
250c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    fallback = (ViewGroup) view;
251c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                }
252c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            }
253c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
254c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            if (view != null) {
255c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                // Else, we will loop and crawl up the view hierarchy and try to find a parent
256c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                final ViewParent parent = view.getParent();
257c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                view = parent instanceof View ? (View) parent : null;
258c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            }
259c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        } while (view != null);
260c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
261c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
262c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        return fallback;
263b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
264b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
265b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
266b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the action to be displayed in this {@link Snackbar}.
267b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
268b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param resId    String resource to display
269b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param listener callback to be invoked when the action is clicked
270b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
271e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
272b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
273b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return setAction(mContext.getText(resId), listener);
274b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
275b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
276b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
277b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the action to be displayed in this {@link Snackbar}.
278b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
279b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param text     Text to display
280b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param listener callback to be invoked when the action is clicked
281b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
282e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
283b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
284b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        final TextView tv = mView.getActionView();
285b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
286b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (TextUtils.isEmpty(text) || listener == null) {
287b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setVisibility(View.GONE);
288b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setOnClickListener(null);
289b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
290b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setVisibility(View.VISIBLE);
291b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setText(text);
292b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setOnClickListener(new View.OnClickListener() {
293b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
294b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onClick(View view) {
295b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    listener.onClick(view);
296b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // Now dismiss the Snackbar
297e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                    dispatchDismiss(Callback.DISMISS_EVENT_ACTION);
298b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
299b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
300b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
301b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
302b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
303b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
304b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
3054112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * Sets the text color of the action specified in
3064112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * {@link #setAction(CharSequence, View.OnClickListener)}.
3074112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     */
308e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3094112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    public Snackbar setActionTextColor(ColorStateList colors) {
3104112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        final TextView tv = mView.getActionView();
3114112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        tv.setTextColor(colors);
3124112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        return this;
3134112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    }
3144112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes
3154112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    /**
3164112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * Sets the text color of the action specified in
3174112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * {@link #setAction(CharSequence, View.OnClickListener)}.
3184112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     */
319e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3205bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public Snackbar setActionTextColor(@ColorInt int color) {
3214112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        final TextView tv = mView.getActionView();
3224112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        tv.setTextColor(color);
3234112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        return this;
3244112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    }
3254112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes
3264112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    /**
327b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Update the text in this {@link Snackbar}.
328b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
329b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param message The new text for the Toast.
330b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
331e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3325bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public Snackbar setText(@NonNull CharSequence message) {
333b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        final TextView tv = mView.getMessageView();
334b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        tv.setText(message);
335b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
336b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
337b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
338b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
339b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Update the text in this {@link Snackbar}.
340b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
341b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param resId The new text for the Toast.
342b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
343e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
344b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setText(@StringRes int resId) {
345b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return setText(mContext.getText(resId));
346b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
347b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
348b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
349b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set how long to show the view for.
350b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
351b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration either be one of the predefined lengths:
352b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
353b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 in milliseconds.
354b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
355e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
356b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setDuration(@Duration int duration) {
357b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mDuration = duration;
358b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
359b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
360b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
361b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
362b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Return the duration.
363b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
364b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
365b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
366b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Duration
367b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public int getDuration() {
368b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mDuration;
369b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
370b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
371b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
372b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Returns the {@link Snackbar}'s view.
373b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
3745bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    @NonNull
375b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public View getView() {
376b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mView;
377b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
378b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
379b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
380b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the {@link Snackbar}.
381b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
382b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void show() {
383b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
384b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
385b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
386b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
387b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Dismiss the {@link Snackbar}.
388b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
389b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void dismiss() {
390e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        dispatchDismiss(Callback.DISMISS_EVENT_MANUAL);
391e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    }
392e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
393e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private void dispatchDismiss(@Callback.DismissEvent int event) {
394e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        SnackbarManager.getInstance().dismiss(mManagerCallback, event);
395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
396b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
397bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    /**
398bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * Set a callback to be called when this the visibility of this {@link Snackbar} changes.
399bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     */
400e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
401e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    public Snackbar setCallback(Callback callback) {
402bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        mCallback = callback;
403e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        return this;
404e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    }
405e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
406e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    /**
4075462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * Return whether this {@link Snackbar} is currently being shown.
408e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes     */
409e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    public boolean isShown() {
4105462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        return SnackbarManager.getInstance().isCurrent(mManagerCallback);
4115462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    }
4125462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
4135462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    /**
4145462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * Returns whether this {@link Snackbar} is currently being shown, or is queued to be
4155462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * shown next.
4165462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     */
4175462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    public boolean isShownOrQueued() {
4185462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
419bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    }
420bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
421b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
422b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
423b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void show() {
424b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
425b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
426b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
427b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
428e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public void dismiss(int event) {
429e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
430b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
431b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    };
432b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
433b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    final void showView() {
434b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mView.getParent() == null) {
435b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final ViewGroup.LayoutParams lp = mView.getLayoutParams();
436b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
437b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (lp instanceof CoordinatorLayout.LayoutParams) {
438b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
439b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
440b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final Behavior behavior = new Behavior();
441b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setStartAlphaSwipeDistance(0.1f);
442b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setEndAlphaSwipeDistance(0.6f);
443b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
444b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
445b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    @Override
446b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    public void onDismiss(View view) {
447e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                        dispatchDismiss(Callback.DISMISS_EVENT_SWIPE);
448b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }
449b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
450b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    @Override
451b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    public void onDragStateChanged(int state) {
452b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        switch (state) {
453b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_DRAGGING:
454b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_SETTLING:
455b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                // If the view is being dragged or settling, cancel the timeout
456b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
457b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                break;
458b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_IDLE:
459b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                // If the view has been released and is idle, restore the timeout
460b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
461b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                break;
462b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
463b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }
464b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                });
465b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ((CoordinatorLayout.LayoutParams) lp).setBehavior(behavior);
466b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
467b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
4685462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            mTargetParent.addView(mView);
469b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
470b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
4715462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mView.setOnAttachStateChangeListener(new SnackbarLayout.OnAttachStateChangeListener() {
4725462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            @Override
4735462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            public void onViewAttachedToWindow(View v) {}
4745462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
4755462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            @Override
4765462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            public void onViewDetachedFromWindow(View v) {
4775462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                if (isShownOrQueued()) {
4785462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // If we haven't already been dismissed then this event is coming from a
4795462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // non-user initiated action. Hence we need to make sure that we callback
4805462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // and keep our state up to date. We need to post the call since removeView()
4815462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // will call through to onDetachedFromWindow and thus overflow.
4825462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    sHandler.post(new Runnable() {
4835462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        @Override
4845462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        public void run() {
4855462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                            onViewHidden(Callback.DISMISS_EVENT_MANUAL);
4865462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        }
4875462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    });
4885462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                }
4895462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
4905462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        });
4915462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
492b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (ViewCompat.isLaidOut(mView)) {
493b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // If the view is already laid out, animate it now
494b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            animateViewIn();
495b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
496b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // Otherwise, add one of our layout change listeners and animate it in when laid out
497b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() {
498b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
499b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onLayoutChange(View view, int left, int top, int right, int bottom) {
500b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    animateViewIn();
501b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    mView.setOnLayoutChangeListener(null);
502b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
503b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
504b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
505b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
506b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
507b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private void animateViewIn() {
508b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
509b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setTranslationY(mView, mView.getHeight());
5103224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.animate(mView)
5113224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    .translationY(0f)
512b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
513b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setDuration(ANIMATION_DURATION)
514b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
515b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
516b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationStart(View view) {
517b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
518b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                    ANIMATION_FADE_DURATION);
519b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
520b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
521b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
522b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationEnd(View view) {
523bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes                            onViewShown();
524b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
525b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }).start();
526b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
5273224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
5283224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    R.anim.design_snackbar_in);
529b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
530b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setDuration(ANIMATION_DURATION);
531b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setAnimationListener(new Animation.AnimationListener() {
532b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
533b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationEnd(Animation animation) {
534bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes                    onViewShown();
535b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
536b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
537b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
538b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationStart(Animation animation) {}
539b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
540b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
541b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationRepeat(Animation animation) {}
542b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
543b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.startAnimation(anim);
544b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
545b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
546b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
547e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private void animateViewOut(final int event) {
548b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
5493224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.animate(mView)
5503224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    .translationY(mView.getHeight())
551b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
552b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setDuration(ANIMATION_DURATION)
553b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
554bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes                        boolean mEndCalled = false;
555bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes
556b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
557b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationStart(View view) {
558b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
559b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
560b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
561b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
562b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationEnd(View view) {
563e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                            onViewHidden(event);
564b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
565b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }).start();
566b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
567a577676a64e5353b8ec927117151aa6be84adf66Chris Banes            Animation anim = AnimationUtils.loadAnimation(mView.getContext(), R.anim.design_snackbar_out);
568b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
569b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setDuration(ANIMATION_DURATION);
570b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setAnimationListener(new Animation.AnimationListener() {
571b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
572b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationEnd(Animation animation) {
573e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                    onViewHidden(event);
574b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
575b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
576b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
577b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationStart(Animation animation) {}
578b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
579b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
580b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationRepeat(Animation animation) {}
581b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
582b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.startAnimation(anim);
583b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
584b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
585b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
586e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    final void hideView(int event) {
587b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mView.getVisibility() != View.VISIBLE || isBeingDragged()) {
588e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            onViewHidden(event);
589b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
590e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            animateViewOut(event);
591b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
592b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
593b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
594bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes    private void onViewShown() {
595bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        SnackbarManager.getInstance().onShown(mManagerCallback);
596bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        if (mCallback != null) {
597bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes            mCallback.onShown(this);
598bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        }
599bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes    }
600bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes
601e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private void onViewHidden(int event) {
602eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        // First tell the SnackbarManager that it has been dismissed
603eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        SnackbarManager.getInstance().onDismissed(mManagerCallback);
604bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        // Now call the dismiss listener (if available)
605bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        if (mCallback != null) {
606e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            mCallback.onDismissed(this, event);
607bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
608eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        // Lastly, remove the view from the parent (if attached)
609eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        final ViewParent parent = mView.getParent();
610eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        if (parent instanceof ViewGroup) {
611eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes            ((ViewGroup) parent).removeView(mView);
612eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        }
613b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
614b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
615b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
616b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @return if the view is being being dragged or settled by {@link SwipeDismissBehavior}.
617b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
618b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private boolean isBeingDragged() {
619b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        final ViewGroup.LayoutParams lp = mView.getLayoutParams();
620b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
621b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (lp instanceof CoordinatorLayout.LayoutParams) {
622b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final CoordinatorLayout.LayoutParams cllp = (CoordinatorLayout.LayoutParams) lp;
623b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final CoordinatorLayout.Behavior behavior = cllp.getBehavior();
624b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
625b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (behavior instanceof SwipeDismissBehavior) {
626b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                return ((SwipeDismissBehavior) behavior).getDragState()
627b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        != SwipeDismissBehavior.STATE_IDLE;
628b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
629b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
630b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return false;
631b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
632b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
633b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
634b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @hide
635b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
636b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static class SnackbarLayout extends LinearLayout {
637b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private TextView mMessageView;
6385cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes        private Button mActionView;
639b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
640b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private int mMaxWidth;
641b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private int mMaxInlineActionWidth;
642b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
643b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        interface OnLayoutChangeListener {
6445462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onLayoutChange(View view, int left, int top, int right, int bottom);
6455462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
6465462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
6475462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        interface OnAttachStateChangeListener {
6485462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onViewAttachedToWindow(View v);
6495462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onViewDetachedFromWindow(View v);
650b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
651b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
652b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private OnLayoutChangeListener mOnLayoutChangeListener;
6535462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        private OnAttachStateChangeListener mOnAttachStateChangeListener;
654b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
655b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public SnackbarLayout(Context context) {
656b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            this(context, null);
657b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
658b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
659b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public SnackbarLayout(Context context, AttributeSet attrs) {
660b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super(context, attrs);
661b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
662b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
663b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMaxInlineActionWidth = a.getDimensionPixelSize(
664b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
6653a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes            if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
6663a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes                ViewCompat.setElevation(this, a.getDimensionPixelSize(
6673a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes                        R.styleable.SnackbarLayout_elevation, 0));
6683a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes            }
669b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            a.recycle();
670b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
671b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            setClickable(true);
672b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
673b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // Now inflate our content. We need to do this manually rather than using an <include>
674b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // in the layout since older versions of the Android do not inflate includes with
675b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // the correct Context.
676a577676a64e5353b8ec927117151aa6be84adf66Chris Banes            LayoutInflater.from(context).inflate(R.layout.design_layout_snackbar_include, this);
6773224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes
6783224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.setAccessibilityLiveRegion(this,
6793224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
680b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
681b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
682b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
683b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onFinishInflate() {
684b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onFinishInflate();
685b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMessageView = (TextView) findViewById(R.id.snackbar_text);
6865cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes            mActionView = (Button) findViewById(R.id.snackbar_action);
687b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
688b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
689b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        TextView getMessageView() {
690b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return mMessageView;
691b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
692b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
6935cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes        Button getActionView() {
694b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return mActionView;
695b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
696b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
697b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
698b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
699b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
700b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
701b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
702b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
703b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
704b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
705b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
706b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final int multiLineVPadding = getResources().getDimensionPixelSize(
707a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                    R.dimen.design_snackbar_padding_vertical_2lines);
708b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final int singleLineVPadding = getResources().getDimensionPixelSize(
709a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                    R.dimen.design_snackbar_padding_vertical);
710b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
711b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
712b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            boolean remeasure = false;
713b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (isMultiLine && mMaxInlineActionWidth > 0
714b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
715b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
716b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        multiLineVPadding - singleLineVPadding)) {
717b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    remeasure = true;
718b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
719b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
720b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
721b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
722b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    remeasure = true;
723b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
724b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
725b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
726b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (remeasure) {
727b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
728b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
729b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
730b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
731b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void animateChildrenIn(int delay, int duration) {
732b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setAlpha(mMessageView, 0f);
733b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
734b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setStartDelay(delay).start();
735b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
736b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mActionView.getVisibility() == VISIBLE) {
737b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(mActionView, 0f);
738b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
739b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        .setStartDelay(delay).start();
740b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
741b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
742b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
743b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void animateChildrenOut(int delay, int duration) {
744b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setAlpha(mMessageView, 1f);
745b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
746b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setStartDelay(delay).start();
747b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
748b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mActionView.getVisibility() == VISIBLE) {
749b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(mActionView, 1f);
750b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
751b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        .setStartDelay(delay).start();
752b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
753b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
754b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
755b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
756b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onLayout(boolean changed, int l, int t, int r, int b) {
757b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onLayout(changed, l, t, r, b);
758b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (changed && mOnLayoutChangeListener != null) {
759b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
760b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
761b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
762b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
7635462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        @Override
7645462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        protected void onAttachedToWindow() {
7655462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            super.onAttachedToWindow();
7665462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            if (mOnAttachStateChangeListener != null) {
7675462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                mOnAttachStateChangeListener.onViewAttachedToWindow(this);
7685462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
7695462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
7705462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
7715462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        @Override
7725462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        protected void onDetachedFromWindow() {
7735462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            super.onDetachedFromWindow();
7745462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            if (mOnAttachStateChangeListener != null) {
7755462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
7765462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
7775462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
7785462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
779b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void setOnLayoutChangeListener(OnLayoutChangeListener onLayoutChangeListener) {
780b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mOnLayoutChangeListener = onLayoutChangeListener;
781b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
782b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
7835462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        void setOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
7845462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            mOnAttachStateChangeListener = listener;
7855462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
7865462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
787b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private boolean updateViewsWithinLayout(final int orientation,
788b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int messagePadTop, final int messagePadBottom) {
789b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            boolean changed = false;
790b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (orientation != getOrientation()) {
791b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                setOrientation(orientation);
792b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                changed = true;
793b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
794b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mMessageView.getPaddingTop() != messagePadTop
795b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    || mMessageView.getPaddingBottom() != messagePadBottom) {
796b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
797b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                changed = true;
798b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
799b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return changed;
800b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
801b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
802b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
803b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (ViewCompat.isPaddingRelative(view)) {
804b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setPaddingRelative(view,
805b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ViewCompat.getPaddingStart(view), topPadding,
806b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ViewCompat.getPaddingEnd(view), bottomPadding);
807b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
808b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                view.setPadding(view.getPaddingLeft(), topPadding,
809b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        view.getPaddingRight(), bottomPadding);
810b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
811b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
812b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
813b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
814b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
815b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
81602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        public boolean canSwipeDismissView(View child) {
81702751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes            return child instanceof SnackbarLayout;
81802751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        }
81902751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes
82002751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        @Override
821b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarLayout child,
822b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                MotionEvent event) {
823b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // We want to make sure that we disable any Snackbar timeouts if the user is
824b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // currently touching the Snackbar. We restore the timeout when complete
825b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
826b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                switch (event.getActionMasked()) {
827b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_DOWN:
828b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
829b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        break;
830b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_UP:
831b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_CANCEL:
832b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
833b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        break;
834b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
835b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
836b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
837b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return super.onInterceptTouchEvent(parent, child, event);
838b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
839b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
840b7f9224b1495db47eb8fd813b5912250e900770aChris Banes}
841