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;
28de0cc188c28f8c19ef5e737e9cd22492b07250c8Tor Norbyeimport android.support.annotation.IntRange;
295bb1f5d910c81ed2a31c687583bd90baccc972acChris Banesimport android.support.annotation.NonNull;
30c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
31b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.annotation.StringRes;
32b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.design.R;
3327705d3451499dd8498effd111f1f4c7df917d87Chris Banesimport android.support.v4.view.OnApplyWindowInsetsListener;
34b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat;
35b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
3627705d3451499dd8498effd111f1f4c7df917d87Chris Banesimport android.support.v4.view.WindowInsetsCompat;
37b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.text.TextUtils;
38b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.util.AttributeSet;
3926c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banesimport android.view.Gravity;
40b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.LayoutInflater;
41b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.MotionEvent;
42b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.View;
43b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.ViewGroup;
44c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banesimport android.view.ViewParent;
45ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banesimport android.view.accessibility.AccessibilityManager;
46b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.animation.Animation;
47b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.animation.AnimationUtils;
485cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banesimport android.widget.Button;
49c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banesimport android.widget.FrameLayout;
50b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.widget.LinearLayout;
51b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.widget.TextView;
52b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
53b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.Retention;
54b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.RetentionPolicy;
55b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
56c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID;
57b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
58b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
59b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/**
60b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Snackbars provide lightweight feedback about an operation. They show a brief message at the
61b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
62b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * elements on screen and only one can be displayed at a time.
63b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * <p>
64b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * They automatically disappear after a timeout or after user interaction elsewhere on the screen,
65b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * particularly after interactions that summon a new surface or activity. Snackbars can be swiped
66b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * off screen.
67b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * <p>
68b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Snackbars can contain an action which is set via
69b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
70bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * <p>
71bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
72bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes * via {@link #setCallback(Callback)}.</p>
73b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */
74e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banespublic final class Snackbar {
75b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
76b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
77bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * Callback class for {@link Snackbar} instances.
78bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     *
79bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * @see Snackbar#setCallback(Callback)
80bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     */
81bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    public static abstract class Callback {
82e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a swipe.*/
83e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_SWIPE = 0;
84e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via an action click.*/
85e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_ACTION = 1;
86e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a timeout.*/
87e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_TIMEOUT = 2;
88e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}.*/
89e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_MANUAL = 3;
90e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** Indicates that the Snackbar was dismissed from a new Snackbar being shown.*/
91e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public static final int DISMISS_EVENT_CONSECUTIVE = 4;
92e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
93e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        /** @hide */
94c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette        @RestrictTo(GROUP_ID)
95e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        @IntDef({DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION, DISMISS_EVENT_TIMEOUT,
96e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                DISMISS_EVENT_MANUAL, DISMISS_EVENT_CONSECUTIVE})
97e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        @Retention(RetentionPolicy.SOURCE)
98e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public @interface DismissEvent {}
99e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
100bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        /**
101bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * Called when the given {@link Snackbar} has been dismissed, either through a time-out,
102bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * having been manually dismissed, or an action being clicked.
103bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         *
104bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @param snackbar The snackbar which has been dismissed.
105e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         * @param event The event which caused the dismissal. One of either:
106e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
107e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
108e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *              {@link #DISMISS_EVENT_CONSECUTIVE}.
109e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes         *
110bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @see Snackbar#dismiss()
111bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         */
112e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public void onDismissed(Snackbar snackbar, @DismissEvent int event) {
113bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes            // empty
114bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
115bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
116bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        /**
117bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * Called when the given {@link Snackbar} is visible.
118bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         *
119bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @param snackbar The snackbar which is now visible.
120bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         * @see Snackbar#show()
121bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes         */
122bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        public void onShown(Snackbar snackbar) {
123bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes            // empty
124bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
125bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    }
126bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
127bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    /**
128b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @hide
129b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
130c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
1310bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
132de0cc188c28f8c19ef5e737e9cd22492b07250c8Tor Norbye    @IntRange(from = 1)
133b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Retention(RetentionPolicy.SOURCE)
134b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public @interface Duration {}
135b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
136b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
1370bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * Show the Snackbar indefinitely. This means that the Snackbar will be displayed from the time
1380bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * that is {@link #show() shown} until either it is dismissed, or another Snackbar is shown.
1390bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     *
1400bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     * @see #setDuration
1410bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes     */
1420bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    public static final int LENGTH_INDEFINITE = -2;
1430bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes
1440bfb0e034ed6b4f7bbf58a111d2fc893e0553350Chris Banes    /**
145b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the Snackbar for a short period of time.
146b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
147b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
148b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
149b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int LENGTH_SHORT = -1;
150b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
151b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
152b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the Snackbar for a long period of time.
153b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
154b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
155b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
156b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static final int LENGTH_LONG = 0;
157b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
158df00f11c8c90db7b26672267fd1b74250c0d6cefChris Banes    static final int ANIMATION_DURATION = 250;
159df00f11c8c90db7b26672267fd1b74250c0d6cefChris Banes    static final int ANIMATION_FADE_DURATION = 180;
160b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
161657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final Handler sHandler;
162657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int MSG_SHOW = 0;
163657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    static final int MSG_DISMISS = 1;
164b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
165b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    static {
166b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
167b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            @Override
168b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            public boolean handleMessage(Message message) {
169b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                switch (message.what) {
170b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MSG_SHOW:
171b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ((Snackbar) message.obj).showView();
172b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        return true;
173b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MSG_DISMISS:
174e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                        ((Snackbar) message.obj).hideView(message.arg1);
175b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        return true;
176b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
177b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                return false;
178b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
179b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        });
180b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
181b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
1825462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    private final ViewGroup mTargetParent;
183b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private final Context mContext;
184657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    final SnackbarLayout mView;
185b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    private int mDuration;
186bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    private Callback mCallback;
187b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
188ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes    private final AccessibilityManager mAccessibilityManager;
189ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes
190e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private Snackbar(ViewGroup parent) {
1915462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mTargetParent = parent;
192b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mContext = parent.getContext();
193b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
194809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes        ThemeUtils.checkAppCompatTheme(mContext);
195809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes
196b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        LayoutInflater inflater = LayoutInflater.from(mContext);
1975462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mView = (SnackbarLayout) inflater.inflate(
1985462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                R.layout.design_layout_snackbar, mTargetParent, false);
199ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes
200ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes        mAccessibilityManager = (AccessibilityManager)
201ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
202b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
203b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
204b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
205c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * Make a Snackbar to display a message
206c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
207c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
208c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
209c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
210c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * whichever comes first.
211c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
212c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
213c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * certain features, such as swipe-to-dismiss and automatically moving of widgets like
214c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * {@link FloatingActionButton}.
215b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
216c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param view     The view to find a parent from.
217b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param text     The text to show.  Can be formatted text.
218b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
219b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 #LENGTH_LONG}
220b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
221e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
2225bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
2235bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes            @Duration int duration) {
224c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        Snackbar snackbar = new Snackbar(findSuitableParent(view));
225b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        snackbar.setText(text);
226b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        snackbar.setDuration(duration);
227b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return snackbar;
228b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
229b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
230b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
231b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Make a Snackbar to display a message.
232b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
233c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given
234c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent,
235c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * which is defined as a {@link CoordinatorLayout} or the window decor's content view,
236c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * whichever comes first.
237c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
238c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable
239c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * certain features, such as swipe-to-dismiss and automatically moving of widgets like
240c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * {@link FloatingActionButton}.
241c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     *
242c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param view     The view to find a parent from.
243c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes     * @param resId    The resource id of the string resource to use. Can be formatted text.
244b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration How long to display the message.  Either {@link #LENGTH_SHORT} or {@link
245b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 #LENGTH_LONG}
246b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
247e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
2485bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
249c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        return make(view, view.getResources().getText(resId), duration);
250c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes    }
251c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
252c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes    private static ViewGroup findSuitableParent(View view) {
253c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        ViewGroup fallback = null;
254c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        do {
255c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            if (view instanceof CoordinatorLayout) {
256c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                // We've found a CoordinatorLayout, use it
257c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                return (ViewGroup) view;
258c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            } else if (view instanceof FrameLayout) {
259c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                if (view.getId() == android.R.id.content) {
260c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // If we've hit the decor content view, then we didn't find a CoL in the
261c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // hierarchy, so use it.
262c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    return (ViewGroup) view;
263c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                } else {
264c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    // It's not the content view but we'll use it as our fallback
265c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                    fallback = (ViewGroup) view;
266c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                }
267c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            }
268c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
269c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            if (view != null) {
270c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                // Else, we will loop and crawl up the view hierarchy and try to find a parent
271c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                final ViewParent parent = view.getParent();
272c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes                view = parent instanceof View ? (View) parent : null;
273c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes            }
274c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        } while (view != null);
275c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes
276c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback
277c482f89070ee5032081e394f77a9a1e63c3cd7a8Chris Banes        return fallback;
278b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
279b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
280b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
281b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the action to be displayed in this {@link Snackbar}.
282b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
283b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param resId    String resource to display
284b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param listener callback to be invoked when the action is clicked
285b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
286e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
287b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
288b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return setAction(mContext.getText(resId), listener);
289b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
290b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
291b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
292b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set the action to be displayed in this {@link Snackbar}.
293b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
294b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param text     Text to display
295b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param listener callback to be invoked when the action is clicked
296b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
297e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
298b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
299b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        final TextView tv = mView.getActionView();
300b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
301b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (TextUtils.isEmpty(text) || listener == null) {
302b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setVisibility(View.GONE);
303b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setOnClickListener(null);
304b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
305b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setVisibility(View.VISIBLE);
306b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setText(text);
307b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            tv.setOnClickListener(new View.OnClickListener() {
308b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
309b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onClick(View view) {
310b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    listener.onClick(view);
311b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // Now dismiss the Snackbar
312e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                    dispatchDismiss(Callback.DISMISS_EVENT_ACTION);
313b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
314b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
315b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
316b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
317b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
318b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
319b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
3204112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * Sets the text color of the action specified in
3214112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * {@link #setAction(CharSequence, View.OnClickListener)}.
3224112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     */
323e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3244112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    public Snackbar setActionTextColor(ColorStateList colors) {
3254112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        final TextView tv = mView.getActionView();
3264112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        tv.setTextColor(colors);
3274112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        return this;
3284112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    }
3294112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes
3304112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    /**
3314112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * Sets the text color of the action specified in
3324112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     * {@link #setAction(CharSequence, View.OnClickListener)}.
3334112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes     */
334e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3355bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public Snackbar setActionTextColor(@ColorInt int color) {
3364112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        final TextView tv = mView.getActionView();
3374112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        tv.setTextColor(color);
3384112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes        return this;
3394112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    }
3404112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes
3414112c61626e5a6107c5874c28829b76bb9fc9f17Chris Banes    /**
342b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Update the text in this {@link Snackbar}.
343b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
344b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param message The new text for the Toast.
345b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
346e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
3475bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    public Snackbar setText(@NonNull CharSequence message) {
348b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        final TextView tv = mView.getMessageView();
349b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        tv.setText(message);
350b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
351b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
352b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
353b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
354b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Update the text in this {@link Snackbar}.
355b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
356b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param resId The new text for the Toast.
357b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
358e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
359b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setText(@StringRes int resId) {
360b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return setText(mContext.getText(resId));
361b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
362b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
363b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
364b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Set how long to show the view for.
365b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
366b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @param duration either be one of the predefined lengths:
367b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 {@link #LENGTH_SHORT}, {@link #LENGTH_LONG}, or a custom duration
368b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *                 in milliseconds.
369b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
370e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
371b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public Snackbar setDuration(@Duration int duration) {
372b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        mDuration = duration;
373b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return this;
374b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
375b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
376b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
377b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Return the duration.
378b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     *
379b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @see #setDuration
380b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
381b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    @Duration
382b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public int getDuration() {
383b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mDuration;
384b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
385b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
386b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
387b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Returns the {@link Snackbar}'s view.
388b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
3895bb1f5d910c81ed2a31c687583bd90baccc972acChris Banes    @NonNull
390b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public View getView() {
391b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        return mView;
392b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
393b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
394b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Show the {@link Snackbar}.
396b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
397b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void show() {
398b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
399b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
400b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
401b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
402b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Dismiss the {@link Snackbar}.
403b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
404b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public void dismiss() {
405e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        dispatchDismiss(Callback.DISMISS_EVENT_MANUAL);
406e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    }
407e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
408657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void dispatchDismiss(@Callback.DismissEvent int event) {
409e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        SnackbarManager.getInstance().dismiss(mManagerCallback, event);
410b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
411b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
412bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    /**
413bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     * Set a callback to be called when this the visibility of this {@link Snackbar} changes.
414bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes     */
415e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    @NonNull
416e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    public Snackbar setCallback(Callback callback) {
417bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        mCallback = callback;
418e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        return this;
419e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    }
420e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes
421e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    /**
4225462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * Return whether this {@link Snackbar} is currently being shown.
423e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes     */
424e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    public boolean isShown() {
4255462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        return SnackbarManager.getInstance().isCurrent(mManagerCallback);
4265462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    }
4275462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
4285462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    /**
4295462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * Returns whether this {@link Snackbar} is currently being shown, or is queued to be
4305462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     * shown next.
4315462d3e588481416a38e893bdb0f1073f82f8dccChris Banes     */
4325462d3e588481416a38e893bdb0f1073f82f8dccChris Banes    public boolean isShownOrQueued() {
4335462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        return SnackbarManager.getInstance().isCurrentOrNext(mManagerCallback);
434bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes    }
435bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes
436657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
437b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
438b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public void show() {
439b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
440b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
441b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
442b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
443e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes        public void dismiss(int event) {
444e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
445b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
446b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    };
447b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
448b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    final void showView() {
449b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (mView.getParent() == null) {
450b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final ViewGroup.LayoutParams lp = mView.getLayoutParams();
451b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
452b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (lp instanceof CoordinatorLayout.LayoutParams) {
453b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior
45426c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes                final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp;
455b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
456b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final Behavior behavior = new Behavior();
457b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setStartAlphaSwipeDistance(0.1f);
458b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setEndAlphaSwipeDistance(0.6f);
459b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
460b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
461b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    @Override
462b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    public void onDismiss(View view) {
463a89b7a8e4840e52dfda1442bcb885686680556f6Chris Banes                        view.setVisibility(View.GONE);
464e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                        dispatchDismiss(Callback.DISMISS_EVENT_SWIPE);
465b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }
466b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
467b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    @Override
468b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    public void onDragStateChanged(int state) {
469b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        switch (state) {
470b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_DRAGGING:
471b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_SETTLING:
472b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                // If the view is being dragged or settling, cancel the timeout
473b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
474b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                break;
475b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                            case SwipeDismissBehavior.STATE_IDLE:
476b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                // If the view has been released and is idle, restore the timeout
477b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
478b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                                break;
479b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
480b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }
481b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                });
48226c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes                clp.setBehavior(behavior);
48326c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes                // Also set the inset edge so that views can dodge the snackbar correctly
4846206ad57276146c8f70e939fd40ce9c7b88767f6Yuichi Araki                clp.insetEdge = Gravity.BOTTOM;
485b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
486b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
4875462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            mTargetParent.addView(mView);
488b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
489b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
4905462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        mView.setOnAttachStateChangeListener(new SnackbarLayout.OnAttachStateChangeListener() {
4915462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            @Override
4925462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            public void onViewAttachedToWindow(View v) {}
4935462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
4945462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            @Override
4955462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            public void onViewDetachedFromWindow(View v) {
4965462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                if (isShownOrQueued()) {
4975462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // If we haven't already been dismissed then this event is coming from a
4985462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // non-user initiated action. Hence we need to make sure that we callback
4995462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // and keep our state up to date. We need to post the call since removeView()
5005462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    // will call through to onDetachedFromWindow and thus overflow.
5015462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    sHandler.post(new Runnable() {
5025462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        @Override
5035462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        public void run() {
5045462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                            onViewHidden(Callback.DISMISS_EVENT_MANUAL);
5055462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                        }
5065462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                    });
5075462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                }
5085462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
5095462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        });
5105462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
511b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (ViewCompat.isLaidOut(mView)) {
512c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            if (shouldAnimate()) {
513c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                // If animations are enabled, animate it in
514c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                animateViewIn();
515c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            } else {
516c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                // Else if anims are disabled just call back now
517c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                onViewShown();
518c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            }
519b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
520c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            // Otherwise, add one of our layout change listeners and show it in when laid out
521b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() {
522b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
523b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onLayoutChange(View view, int left, int top, int right, int bottom) {
524b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    mView.setOnLayoutChangeListener(null);
525c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes
526c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                    if (shouldAnimate()) {
527c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                        // If animations are enabled, animate it in
528c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                        animateViewIn();
529c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                    } else {
530c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                        // Else if anims are disabled just call back now
531c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                        onViewShown();
532c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                    }
533b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
534b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
535b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
536b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
537b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
538657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void animateViewIn() {
539b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
540b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setTranslationY(mView, mView.getHeight());
5413224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.animate(mView)
5423224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    .translationY(0f)
543b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
544b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setDuration(ANIMATION_DURATION)
545b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
546b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
547b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationStart(View view) {
548c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                            mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
549c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                                    ANIMATION_FADE_DURATION);
550b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
551b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
552b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
553b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationEnd(View view) {
554bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes                            onViewShown();
555b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
556b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }).start();
557b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
5583224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
5593224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    R.anim.design_snackbar_in);
560b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
561b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setDuration(ANIMATION_DURATION);
562b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setAnimationListener(new Animation.AnimationListener() {
563b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
564b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationEnd(Animation animation) {
565bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes                    onViewShown();
566b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
567b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
568b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
569b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationStart(Animation animation) {}
570b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
571b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
572b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationRepeat(Animation animation) {}
573b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
574b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.startAnimation(anim);
575b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
576b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
577b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
578e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes    private void animateViewOut(final int event) {
579b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
5803224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.animate(mView)
5813224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    .translationY(mView.getHeight())
582b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
583b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setDuration(ANIMATION_DURATION)
584b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setListener(new ViewPropertyAnimatorListenerAdapter() {
585b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
586b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationStart(View view) {
587c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                            mView.animateChildrenOut(0, ANIMATION_FADE_DURATION);
588b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
589b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
590b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        @Override
591b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        public void onAnimationEnd(View view) {
592e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                            onViewHidden(event);
593b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        }
594b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    }).start();
595b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        } else {
596c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
597c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes                    R.anim.design_snackbar_out);
598b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
599b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setDuration(ANIMATION_DURATION);
600b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            anim.setAnimationListener(new Animation.AnimationListener() {
601b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
602b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationEnd(Animation animation) {
603e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes                    onViewHidden(event);
604b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
605b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
606b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
607b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationStart(Animation animation) {}
608b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
609b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                @Override
610b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                public void onAnimationRepeat(Animation animation) {}
611b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            });
612b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mView.startAnimation(anim);
613b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
614b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
615b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
616a89b7a8e4840e52dfda1442bcb885686680556f6Chris Banes    final void hideView(@Callback.DismissEvent final int event) {
617c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes        if (shouldAnimate() && mView.getVisibility() == View.VISIBLE) {
618e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            animateViewOut(event);
619c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes        } else {
620c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            // If anims are disabled or the view isn't visible, just call back now
621c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes            onViewHidden(event);
622b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
623b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
624b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
625657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void onViewShown() {
626bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        SnackbarManager.getInstance().onShown(mManagerCallback);
627bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        if (mCallback != null) {
628bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes            mCallback.onShown(this);
629bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes        }
630bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes    }
631bfd48d0521963754e04e407499ee9e278fe06c0fChris Banes
632657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    void onViewHidden(int event) {
633eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        // First tell the SnackbarManager that it has been dismissed
634eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        SnackbarManager.getInstance().onDismissed(mManagerCallback);
635bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        // Now call the dismiss listener (if available)
636bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        if (mCallback != null) {
637e51e533995e445f031fc8efcce6ff9a61e7066ccChris Banes            mCallback.onDismissed(this, event);
638bfae5eb86f8c5e63b33b6d36073f2230599a716bChris Banes        }
63926c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes        if (Build.VERSION.SDK_INT < 11) {
64026c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            // We need to hide the Snackbar on pre-v11 since it uses an old style Animation.
64126c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            // ViewGroup has special handling in removeView() when getAnimation() != null in
64226c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            // that it waits. This then means that the calculated insets are wrong and the
64326c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            // any dodging views do not return. We workaround it by setting the view to gone while
64426c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            // ViewGroup actually gets around to removing it.
64526c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes            mView.setVisibility(View.GONE);
64626c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes        }
64726c07a84cf70ddf44516f7009fe4ce3b5ed65513Chris Banes        // Lastly, hide and remove the view from the parent (if attached)
648eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        final ViewParent parent = mView.getParent();
649eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        if (parent instanceof ViewGroup) {
650eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes            ((ViewGroup) parent).removeView(mView);
651eeea73a484fe384e6a8059e45db369cea7c247f6Chris Banes        }
652b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
653b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
654b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    /**
655c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes     * Returns true if we should animate the Snackbar view in/out.
656c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes     */
657657ea1100fee4750f148f9d0dcb7e7e2028f105eAurimas Liutikas    boolean shouldAnimate() {
658c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes        return !mAccessibilityManager.isEnabled();
659c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes    }
660c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes
661c6cfc5e717bb9c12c6a789a2dcd2badb90c80ed6Chris Banes    /**
662b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * @hide
663b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
664c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette    @RestrictTo(GROUP_ID)
665b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static class SnackbarLayout extends LinearLayout {
666b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private TextView mMessageView;
6675cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes        private Button mActionView;
668b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
669b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private int mMaxWidth;
670b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private int mMaxInlineActionWidth;
671b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
672b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        interface OnLayoutChangeListener {
6735462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onLayoutChange(View view, int left, int top, int right, int bottom);
6745462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
6755462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
6765462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        interface OnAttachStateChangeListener {
6775462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onViewAttachedToWindow(View v);
6785462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            void onViewDetachedFromWindow(View v);
679b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
680b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
681b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private OnLayoutChangeListener mOnLayoutChangeListener;
6825462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        private OnAttachStateChangeListener mOnAttachStateChangeListener;
683b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
684b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public SnackbarLayout(Context context) {
685b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            this(context, null);
686b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
687b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
688b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public SnackbarLayout(Context context, AttributeSet attrs) {
689b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super(context, attrs);
690b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SnackbarLayout);
691b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMaxWidth = a.getDimensionPixelSize(R.styleable.SnackbarLayout_android_maxWidth, -1);
692b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMaxInlineActionWidth = a.getDimensionPixelSize(
693b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
6943a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes            if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
6953a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes                ViewCompat.setElevation(this, a.getDimensionPixelSize(
6963a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes                        R.styleable.SnackbarLayout_elevation, 0));
6973a2c9c408e240e761b043139c5d228d3ad93f6c2Chris Banes            }
698b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            a.recycle();
699b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
700b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            setClickable(true);
701b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
702b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // Now inflate our content. We need to do this manually rather than using an <include>
703b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // in the layout since older versions of the Android do not inflate includes with
704b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // the correct Context.
705a577676a64e5353b8ec927117151aa6be84adf66Chris Banes            LayoutInflater.from(context).inflate(R.layout.design_layout_snackbar_include, this);
7063224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes
7073224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes            ViewCompat.setAccessibilityLiveRegion(this,
7083224093af46bac6f2fd5b372d5fbd56429b811c1Chris Banes                    ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
709ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes            ViewCompat.setImportantForAccessibility(this,
710ed149aee7e5d78a9b5714e7b7b1cc394ca9e4af3Chris Banes                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
71127705d3451499dd8498effd111f1f4c7df917d87Chris Banes
71227705d3451499dd8498effd111f1f4c7df917d87Chris Banes            // Make sure that we fit system windows and have a listener to apply any insets
71327705d3451499dd8498effd111f1f4c7df917d87Chris Banes            ViewCompat.setFitsSystemWindows(this, true);
71427705d3451499dd8498effd111f1f4c7df917d87Chris Banes            ViewCompat.setOnApplyWindowInsetsListener(this,
71527705d3451499dd8498effd111f1f4c7df917d87Chris Banes                    new android.support.v4.view.OnApplyWindowInsetsListener() {
71627705d3451499dd8498effd111f1f4c7df917d87Chris Banes                @Override
71727705d3451499dd8498effd111f1f4c7df917d87Chris Banes                public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
71827705d3451499dd8498effd111f1f4c7df917d87Chris Banes                    // Copy over the bottom inset as padding so that we're displayed above the
71927705d3451499dd8498effd111f1f4c7df917d87Chris Banes                    // navigation bar
72027705d3451499dd8498effd111f1f4c7df917d87Chris Banes                    v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
72127705d3451499dd8498effd111f1f4c7df917d87Chris Banes                            v.getPaddingRight(), insets.getSystemWindowInsetBottom());
72227705d3451499dd8498effd111f1f4c7df917d87Chris Banes                    return insets;
72327705d3451499dd8498effd111f1f4c7df917d87Chris Banes                }
72427705d3451499dd8498effd111f1f4c7df917d87Chris Banes            });
725b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
726b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
727b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
728b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onFinishInflate() {
729b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onFinishInflate();
730b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mMessageView = (TextView) findViewById(R.id.snackbar_text);
7315cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes            mActionView = (Button) findViewById(R.id.snackbar_action);
732b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
733b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
734b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        TextView getMessageView() {
735b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return mMessageView;
736b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
737b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
7385cf1a7e04097b7e116c118cfa823a4cdb48f38a1Chris Banes        Button getActionView() {
739b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return mActionView;
740b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
741b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
742b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
743b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
744b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
745b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
746b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
747b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth, MeasureSpec.EXACTLY);
748b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
749b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
750b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
751b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final int multiLineVPadding = getResources().getDimensionPixelSize(
752a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                    R.dimen.design_snackbar_padding_vertical_2lines);
753b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final int singleLineVPadding = getResources().getDimensionPixelSize(
754a577676a64e5353b8ec927117151aa6be84adf66Chris Banes                    R.dimen.design_snackbar_padding_vertical);
755b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
756b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
757b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            boolean remeasure = false;
758b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (isMultiLine && mMaxInlineActionWidth > 0
759b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    && mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
760b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
761b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        multiLineVPadding - singleLineVPadding)) {
762b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    remeasure = true;
763b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
764b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
765b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int messagePadding = isMultiLine ? multiLineVPadding : singleLineVPadding;
766b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (updateViewsWithinLayout(HORIZONTAL, messagePadding, messagePadding)) {
767b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    remeasure = true;
768b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
769b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
770b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
771b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (remeasure) {
772b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
773b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
774b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
775b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
776b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void animateChildrenIn(int delay, int duration) {
777b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setAlpha(mMessageView, 0f);
778b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
779b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setStartDelay(delay).start();
780b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
781b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mActionView.getVisibility() == VISIBLE) {
782b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(mActionView, 0f);
783b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
784b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        .setStartDelay(delay).start();
785b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
786b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
787b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
788b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void animateChildrenOut(int delay, int duration) {
789b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.setAlpha(mMessageView, 1f);
790b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
791b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    .setStartDelay(delay).start();
792b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
793b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mActionView.getVisibility() == VISIBLE) {
794b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setAlpha(mActionView, 1f);
795b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
796b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        .setStartDelay(delay).start();
797b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
798b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
799b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
800b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
801b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        protected void onLayout(boolean changed, int l, int t, int r, int b) {
802b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            super.onLayout(changed, l, t, r, b);
803819bbaf04bba63abc7b329a14c92af52515aae0aChris Banes            if (mOnLayoutChangeListener != null) {
804b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
805b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
806b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
807b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
8085462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        @Override
8095462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        protected void onAttachedToWindow() {
8105462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            super.onAttachedToWindow();
8115462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            if (mOnAttachStateChangeListener != null) {
8125462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                mOnAttachStateChangeListener.onViewAttachedToWindow(this);
8135462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
81427705d3451499dd8498effd111f1f4c7df917d87Chris Banes
81527705d3451499dd8498effd111f1f4c7df917d87Chris Banes            ViewCompat.requestApplyInsets(this);
8165462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
8175462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
8185462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        @Override
8195462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        protected void onDetachedFromWindow() {
8205462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            super.onDetachedFromWindow();
8215462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            if (mOnAttachStateChangeListener != null) {
8225462d3e588481416a38e893bdb0f1073f82f8dccChris Banes                mOnAttachStateChangeListener.onViewDetachedFromWindow(this);
8235462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            }
8245462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
8255462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
826b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        void setOnLayoutChangeListener(OnLayoutChangeListener onLayoutChangeListener) {
827b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            mOnLayoutChangeListener = onLayoutChangeListener;
828b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
829b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
8305462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        void setOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
8315462d3e588481416a38e893bdb0f1073f82f8dccChris Banes            mOnAttachStateChangeListener = listener;
8325462d3e588481416a38e893bdb0f1073f82f8dccChris Banes        }
8335462d3e588481416a38e893bdb0f1073f82f8dccChris Banes
834b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private boolean updateViewsWithinLayout(final int orientation,
835b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                final int messagePadTop, final int messagePadBottom) {
836b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            boolean changed = false;
837b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (orientation != getOrientation()) {
838b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                setOrientation(orientation);
839b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                changed = true;
840b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
841b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (mMessageView.getPaddingTop() != messagePadTop
842b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    || mMessageView.getPaddingBottom() != messagePadBottom) {
843b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                updateTopBottomPadding(mMessageView, messagePadTop, messagePadBottom);
844b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                changed = true;
845b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
846b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return changed;
847b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
848b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
849b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private static void updateTopBottomPadding(View view, int topPadding, int bottomPadding) {
850b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (ViewCompat.isPaddingRelative(view)) {
851b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.setPaddingRelative(view,
852b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ViewCompat.getPaddingStart(view), topPadding,
853b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        ViewCompat.getPaddingEnd(view), bottomPadding);
854b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            } else {
855b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                view.setPadding(view.getPaddingLeft(), topPadding,
856b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        view.getPaddingRight(), bottomPadding);
857b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
858b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
859b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
860b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
861b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
862b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
86302751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        public boolean canSwipeDismissView(View child) {
86402751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes            return child instanceof SnackbarLayout;
86502751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        }
86602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes
86702751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes        @Override
868b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean onInterceptTouchEvent(CoordinatorLayout parent, SnackbarLayout child,
869b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                MotionEvent event) {
870b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // We want to make sure that we disable any Snackbar timeouts if the user is
871b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            // currently touching the Snackbar. We restore the timeout when complete
872b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (parent.isPointInChildBounds(child, (int) event.getX(), (int) event.getY())) {
873b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                switch (event.getActionMasked()) {
874b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_DOWN:
875b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        SnackbarManager.getInstance().cancelTimeout(mManagerCallback);
876b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        break;
877b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_UP:
878b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    case MotionEvent.ACTION_CANCEL:
879b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        SnackbarManager.getInstance().restoreTimeout(mManagerCallback);
880b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                        break;
881b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
882b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
883b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
884b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return super.onInterceptTouchEvent(parent, child, event);
885b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
886b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
887b7f9224b1495db47eb8fd813b5912250e900770aChris Banes}
888