1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/* 2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project 3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License"); 5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License. 6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at 7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * http://www.apache.org/licenses/LICENSE-2.0 9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software 11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS, 12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and 14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License. 15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.ui; 17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context; 19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Point; 20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.graphics.Rect; 21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Handler; 22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils; 23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.DisplayMetrics; 24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.Gravity; 25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.MotionEvent; 26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View; 27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View.MeasureSpec; 28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View.OnTouchListener; 29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewGroup; 30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewGroup.LayoutParams; 31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewPropertyAnimator; 32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewTreeObserver.OnGlobalLayoutListener; 33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.WindowManager; 34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.PopupWindow; 35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.PopupWindow.OnDismissListener; 36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.Factory; 38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R; 39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.SnackBar.Placement; 40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.SnackBar.SnackBarListener; 41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.AccessibilityUtil; 42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert; 43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil; 44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil; 45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.TextUtil; 46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils; 47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.base.Joiner; 48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List; 50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class SnackBarManager { 52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private static SnackBarManager sInstance; 54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public static SnackBarManager get() { 56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sInstance == null) { 57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd synchronized (SnackBarManager.class) { 58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (sInstance == null) { 59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd sInstance = new SnackBarManager(); 60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return sInstance; 64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Runnable mDismissRunnable = new Runnable() { 67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dismiss(); 70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final OnTouchListener mDismissOnTouchListener = new OnTouchListener() { 74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public boolean onTouch(final View view, final MotionEvent event) { 76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Dismiss the {@link SnackBar} but don't consume the event. 77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dismiss(); 78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return false; 79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final SnackBarListener mDismissOnUserTapListener = new SnackBarListener() { 83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onActionClick() { 85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dismiss(); 86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final int mTranslationDurationMs; 90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private final Handler mHideHandler; 91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SnackBar mCurrentSnackBar; 93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SnackBar mLatestSnackBar; 94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SnackBar mNextSnackBar; 95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private boolean mIsCurrentlyDismissing; 96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private PopupWindow mPopupWindow; 97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private SnackBarManager() { 99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mTranslationDurationMs = Factory.get().getApplicationContext().getResources().getInteger( 100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd R.integer.snackbar_translation_duration_ms); 101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideHandler = new Handler(); 102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SnackBar getLatestSnackBar() { 105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return mLatestSnackBar; 106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public SnackBar.Builder newBuilder(final View parentView) { 109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return new SnackBar.Builder(this, parentView); 110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * The given snackBar is not guaranteed to be shown. If the previous snackBar is animating away, 114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * and another snackBar is requested to show after this one, this snackBar will be skipped. 115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void show(final SnackBar snackBar) { 117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(snackBar); 118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mCurrentSnackBar != null) { 120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(LogUtil.BUGLE_TAG, "Showing snack bar, but currentSnackBar was not null."); 121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Dismiss the current snack bar. That will cause the next snack bar to be shown on 123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // completion. 124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mNextSnackBar = snackBar; 125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mLatestSnackBar = snackBar; 126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd dismiss(); 127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCurrentSnackBar = snackBar; 131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mLatestSnackBar = snackBar; 132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We want to know when either button was tapped so we can dismiss. 134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBar.setListener(mDismissOnUserTapListener); 135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Cancel previous dismisses & set dismiss for the delay time. 137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideHandler.removeCallbacks(mDismissRunnable); 138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideHandler.postDelayed(mDismissRunnable, snackBar.getDuration()); 139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBar.setEnabled(false); 141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // For some reason, the addView function does not respect layoutParams. 143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // We need to explicitly set it first here. 144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View rootView = snackBar.getRootView(); 145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.DEBUG)) { 147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(LogUtil.BUGLE_TAG, "Showing snack bar: " + snackBar); 148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Measure the snack bar root view so we know how much to translate by. 150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd measureSnackBar(snackBar); 151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow = new PopupWindow(snackBar.getContext()); 152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.setWidth(LayoutParams.MATCH_PARENT); 153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.setHeight(LayoutParams.WRAP_CONTENT); 154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.setBackgroundDrawable(null); 155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.setContentView(rootView); 156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Placement placement = snackBar.getPlacement(); 157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (placement == null) { 158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.showAtLocation( 159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBar.getParentView(), Gravity.BOTTOM | Gravity.START, 160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0, getScreenBottomOffset(snackBar)); 161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View anchorView = placement.getAnchorView(); 163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // You'd expect PopupWindow.showAsDropDown to ensure the popup moves with the anchor 165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // view, which it does for scrolling, but not layout changes, so we have to manually 166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // update while the snackbar is showing 167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() { 168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onGlobalLayout() { 170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.update(anchorView, 0, getRelativeOffset(snackBar), 171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd anchorView.getWidth(), LayoutParams.WRAP_CONTENT); 172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }; 174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd anchorView.getViewTreeObserver().addOnGlobalLayoutListener(listener); 175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.setOnDismissListener(new OnDismissListener() { 176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void onDismiss() { 178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd anchorView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); 179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.showAsDropDown(anchorView, 0, getRelativeOffset(snackBar)); 182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Animate the toast bar into view. 186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd placeSnackBarOffScreen(snackBar); 187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd animateSnackBarOnScreen(snackBar).withEndAction(new Runnable() { 188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCurrentSnackBar.setEnabled(true); 191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd makeCurrentSnackBarDismissibleOnTouch(); 192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Fire an accessibility event as needed 193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd String snackBarText = snackBar.getMessageText(); 194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtils.isEmpty(snackBarText) && 195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd TextUtils.getTrimmedLength(snackBarText) > 0) { 196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBarText = snackBarText.trim(); 197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final String snackBarActionText = snackBar.getActionLabel(); 198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (!TextUtil.isAllWhitespace(snackBarActionText)) { 199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBarText = Joiner.on(", ").join(snackBarText, snackBarActionText); 200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd AccessibilityUtil.announceForAccessibilityCompat(snackBar.getSnackBarView(), 202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd null /*accessibilityManager*/, snackBarText); 203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Animate any interaction views out of the way. 208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd animateInteractionsOnShow(snackBar); 209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Dismisses the current toast that is showing. If there is a toast waiting to be shown, that 213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * toast will be shown when the current one has been dismissed. 214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void dismiss() { 216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mHideHandler.removeCallbacks(mDismissRunnable); 217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mCurrentSnackBar == null || mIsCurrentlyDismissing) { 219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return; 220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SnackBar snackBar = mCurrentSnackBar; 223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd LogUtil.d(LogUtil.BUGLE_TAG, "Dismissing snack bar."); 225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mIsCurrentlyDismissing = true; 226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBar.setEnabled(false); 228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Animate the toast bar down. 230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View rootView = snackBar.getRootView(); 231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd animateSnackBarOffScreen(snackBar).withEndAction(new Runnable() { 232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd @Override 233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd public void run() { 234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd rootView.setVisibility(View.GONE); 235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd try { 236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mPopupWindow.dismiss(); 237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } catch (IllegalArgumentException e) { 238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // PopupWindow.dismiss() will fire an IllegalArgumentException if the activity 239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // has already ended while we were animating 240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCurrentSnackBar = null; 243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mIsCurrentlyDismissing = false; 244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Show the next toast if one is waiting. 246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (mNextSnackBar != null) { 247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final SnackBar localNextSnackBar = mNextSnackBar; 248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mNextSnackBar = null; 249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd show(localNextSnackBar); 250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd }); 253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Animate any interaction views back. 255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd animateInteractionsOnDismiss(snackBar); 256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void makeCurrentSnackBarDismissibleOnTouch() { 259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Set touching on the entire view, the {@link SnackBar} itself, as 260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // well as the button's dismiss the toast. 261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCurrentSnackBar.getRootView().setOnTouchListener(mDismissOnTouchListener); 262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd mCurrentSnackBar.getSnackBarView().setOnTouchListener(mDismissOnTouchListener); 263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void measureSnackBar(final SnackBar snackBar) { 266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View rootView = snackBar.getRootView(); 267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Point displaySize = new Point(); 268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd getWindowManager(snackBar.getContext()).getDefaultDisplay().getSize(displaySize); 269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int widthSpec = ViewGroup.getChildMeasureSpec( 270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MeasureSpec.makeMeasureSpec(displaySize.x, MeasureSpec.EXACTLY), 271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0, LayoutParams.MATCH_PARENT); 272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int heightSpec = ViewGroup.getChildMeasureSpec( 273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd MeasureSpec.makeMeasureSpec(displaySize.y, MeasureSpec.EXACTLY), 274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 0, LayoutParams.WRAP_CONTENT); 275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd rootView.measure(widthSpec, heightSpec); 276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void placeSnackBarOffScreen(final SnackBar snackBar) { 279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View rootView = snackBar.getRootView(); 280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View snackBarView = snackBar.getSnackBarView(); 281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBarView.setTranslationY(rootView.getMeasuredHeight()); 282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ViewPropertyAnimator animateSnackBarOnScreen(final SnackBar snackBar) { 285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View snackBarView = snackBar.getSnackBarView(); 286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return normalizeAnimator(snackBarView.animate()).translationX(0).translationY(0); 287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ViewPropertyAnimator animateSnackBarOffScreen(final SnackBar snackBar) { 290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View rootView = snackBar.getRootView(); 291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View snackBarView = snackBar.getSnackBarView(); 292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return normalizeAnimator(snackBarView.animate()).translationY(rootView.getHeight()); 293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void animateInteractionsOnShow(final SnackBar snackBar) { 296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<SnackBarInteraction> interactions = snackBar.getInteractions(); 297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final SnackBarInteraction interaction : interactions) { 298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (interaction != null) { 299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ViewPropertyAnimator animator = interaction.animateOnSnackBarShow(snackBar); 300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (animator != null) { 301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd normalizeAnimator(animator); 302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private void animateInteractionsOnDismiss(final SnackBar snackBar) { 308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final List<SnackBarInteraction> interactions = snackBar.getInteractions(); 309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd for (final SnackBarInteraction interaction : interactions) { 310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (interaction != null) { 311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final ViewPropertyAnimator animator = 312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd interaction.animateOnSnackBarDismiss(snackBar); 313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (animator != null) { 314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd normalizeAnimator(animator); 315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private ViewPropertyAnimator normalizeAnimator(final ViewPropertyAnimator animator) { 321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return animator 322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setInterpolator(UiUtils.DEFAULT_INTERPOLATOR) 323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd .setDuration(mTranslationDurationMs); 324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private WindowManager getWindowManager(final Context context) { 327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd /** 331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Get the offset from the bottom of the screen where the snack bar should be placed. 332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */ 333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int getScreenBottomOffset(final SnackBar snackBar) { 334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final WindowManager windowManager = getWindowManager(snackBar.getContext()); 335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final DisplayMetrics displayMetrics = new DisplayMetrics(); 336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL()) { 337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd windowManager.getDefaultDisplay().getRealMetrics(displayMetrics); 338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd windowManager.getDefaultDisplay().getMetrics(displayMetrics); 340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final int screenHeight = displayMetrics.heightPixels; 342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (OsUtil.isAtLeastL()) { 344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // In L, the navigation bar is included in the space for the popup window, so we have to 345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // offset by the size of the navigation bar 346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Rect displayRect = new Rect(); 347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd snackBar.getParentView().getRootView().getWindowVisibleDisplayFrame(displayRect); 348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return screenHeight - displayRect.bottom; 349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd 354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd private int getRelativeOffset(final SnackBar snackBar) { 355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final Placement placement = snackBar.getPlacement(); 356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd Assert.notNull(placement); 357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd final View anchorView = placement.getAnchorView(); 358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd if (placement.getAnchorAbove()) { 359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return -snackBar.getRootView().getMeasuredHeight() - anchorView.getHeight(); 360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } else { 361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd // Use the default dropdown positioning 362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd return 0; 363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd } 365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd} 366