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