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