OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.snackbar; | 5 package org.chromium.chrome.browser.snackbar; |
6 | 6 |
7 import android.annotation.TargetApi; | |
8 import android.app.Activity; | |
9 import android.content.Context; | |
7 import android.graphics.Rect; | 10 import android.graphics.Rect; |
11 import android.os.Build; | |
8 import android.os.Handler; | 12 import android.os.Handler; |
13 import android.util.AttributeSet; | |
9 import android.view.Gravity; | 14 import android.view.Gravity; |
10 import android.view.View; | 15 import android.view.View; |
11 import android.view.View.OnClickListener; | 16 import android.view.View.OnClickListener; |
12 import android.view.ViewTreeObserver.OnGlobalLayoutListener; | 17 import android.view.ViewTreeObserver.OnGlobalLayoutListener; |
13 import android.view.Window; | 18 import android.widget.LinearLayout; |
14 | 19 |
15 import org.chromium.base.ApiCompatibilityUtils; | 20 import org.chromium.base.ApiCompatibilityUtils; |
16 import org.chromium.base.VisibleForTesting; | 21 import org.chromium.base.VisibleForTesting; |
17 import org.chromium.chrome.R; | 22 import org.chromium.chrome.R; |
18 import org.chromium.chrome.browser.ChromeActivity; | 23 import org.chromium.chrome.browser.ChromeActivity; |
19 import org.chromium.chrome.browser.device.DeviceClassManager; | 24 import org.chromium.chrome.browser.device.DeviceClassManager; |
20 import org.chromium.ui.UiUtils; | 25 import org.chromium.ui.UiUtils; |
21 import org.chromium.ui.base.DeviceFormFactor; | 26 import org.chromium.ui.base.DeviceFormFactor; |
22 | 27 |
23 import java.util.HashSet; | 28 import java.util.HashSet; |
24 import java.util.Stack; | 29 import java.util.Stack; |
25 | 30 |
26 /** | 31 /** |
27 * Manager for the snackbar showing at the bottom of activity. | 32 * Manager for the snackbar showing at the bottom of activity. |
28 * <p/> | 33 * <p/> |
29 * There should be only one SnackbarManager and one snackbar in the activity. Th e manager maintains | 34 * There should be only one SnackbarManager and one snackbar in the activity. Th e manager maintains |
30 * a stack to store all entries that should be displayed. When showing a new sna ckbar, old entry | 35 * a stack to store all entries that should be displayed. When showing a new sna ckbar, old entry |
31 * will be pushed to stack and text/button will be updated to the newest entry. | 36 * will be pushed to stack and text/button will be updated to the newest entry. |
32 * <p/> | 37 * <p/> |
33 * When action button is clicked, this manager will call | 38 * When action button is clicked, this manager will call |
34 * {@link SnackbarController#onAction(Object)} in corresponding listener, and sh ow the next | 39 * {@link SnackbarController#onAction(Object)} in corresponding listener, and sh ow the next |
35 * entry in stack. Otherwise if no action is taken by user during | 40 * entry in stack. Otherwise if no action is taken by user during |
36 * {@link #DEFAULT_SNACKBAR_DURATION_MS} milliseconds, it will clear the stack a nd call | 41 * {@link #DEFAULT_SNACKBAR_DURATION_MS} milliseconds, it will clear the stack a nd call |
37 * {@link SnackbarController#onDismissNoAction(Object)} to all listeners. | 42 * {@link SnackbarController#onDismissNoAction(Object)} to all listeners. |
38 */ | 43 */ |
39 public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener { | 44 public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener { |
40 | 45 |
46 private static RuntimeException sWindowDetachTrace; | |
47 | |
48 /** | |
49 * A {@link LinearLayout} that logs the stack trace when {@link #onDetachedF romWindow()} is | |
50 * called. | |
51 */ | |
52 public static class WindowDismissalAwareLayout extends LinearLayout { | |
53 // TODO(ianwen): remove this class after crbug.com/553569 is fixed. | |
54 /** | |
55 * Constructor for XML inflation. | |
56 */ | |
57 public WindowDismissalAwareLayout(Context context, AttributeSet attrs) { | |
58 super(context, attrs); | |
59 } | |
60 | |
61 @Override | |
62 protected void onDetachedFromWindow() { | |
63 super.onDetachedFromWindow(); | |
64 sWindowDetachTrace = new RuntimeException( | |
65 "Stacktrace for Snackbar view to be detached from window"); | |
66 } | |
67 } | |
68 | |
41 /** | 69 /** |
42 * Interface that shows the ability to provide a unified snackbar manager. | 70 * Interface that shows the ability to provide a unified snackbar manager. |
43 */ | 71 */ |
44 public interface SnackbarManageable { | 72 public interface SnackbarManageable { |
45 /** | 73 /** |
46 * @return The snackbar manager that has a proper anchor view. | 74 * @return The snackbar manager that has a proper anchor view. |
47 */ | 75 */ |
48 SnackbarManager getSnackbarManager(); | 76 SnackbarManager getSnackbarManager(); |
49 } | 77 } |
50 | 78 |
(...skipping 24 matching lines...) Expand all Loading... | |
75 * Notify each SnackbarControllers instance only once immediately before the snackbar is | 103 * Notify each SnackbarControllers instance only once immediately before the snackbar is |
76 * dismissed. This function is likely to be used for controllers to do u ser metrics for | 104 * dismissed. This function is likely to be used for controllers to do u ser metrics for |
77 * dismissal. | 105 * dismissal. |
78 * @param isTimeout Whether this dismissal is triggered by timeout. | 106 * @param isTimeout Whether this dismissal is triggered by timeout. |
79 */ | 107 */ |
80 void onDismissForEachType(boolean isTimeout); | 108 void onDismissForEachType(boolean isTimeout); |
81 } | 109 } |
82 | 110 |
83 private static final int DEFAULT_SNACKBAR_DURATION_MS = 3000; | 111 private static final int DEFAULT_SNACKBAR_DURATION_MS = 3000; |
84 private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000; | 112 private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000; |
113 private static final String TAG = "cr_snackbar"; | |
85 | 114 |
86 // Used instead of the constant so tests can override the value. | 115 // Used instead of the constant so tests can override the value. |
87 private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS; | 116 private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS; |
88 private static int sAccessibilitySnackbarDurationMs = ACCESSIBILITY_MODE_SNA CKBAR_DURATION_MS; | 117 private static int sAccessibilitySnackbarDurationMs = ACCESSIBILITY_MODE_SNA CKBAR_DURATION_MS; |
89 | 118 |
90 private final boolean mIsTablet; | 119 private final boolean mIsTablet; |
91 | 120 |
121 private Activity mActivity; | |
92 private View mDecor; | 122 private View mDecor; |
93 private final Handler mUIThreadHandler; | 123 private final Handler mUIThreadHandler; |
94 private Stack<Snackbar> mStack = new Stack<Snackbar>(); | 124 private Stack<Snackbar> mStack = new Stack<Snackbar>(); |
95 private SnackbarPopupWindow mPopup; | 125 private SnackbarPopupWindow mPopup; |
96 private final Runnable mHideRunnable = new Runnable() { | 126 private final Runnable mHideRunnable = new Runnable() { |
97 @Override | 127 @Override |
98 public void run() { | 128 public void run() { |
99 dismissAllSnackbars(true); | 129 dismissAllSnackbars(true); |
100 } | 130 } |
101 }; | 131 }; |
102 | 132 |
103 // Variables used and reused in local calculations. | 133 // Variables used and reused in local calculations. |
104 private int[] mTempDecorPosition = new int[2]; | 134 private int[] mTempDecorPosition = new int[2]; |
105 private Rect mTempVisibleDisplayFrame = new Rect(); | 135 private Rect mTempVisibleDisplayFrame = new Rect(); |
106 | 136 |
107 /** | 137 /** |
108 * Constructs a SnackbarManager to show snackbars in the given window. | 138 * Constructs a SnackbarManager to show snackbars in the given window. |
109 */ | 139 */ |
110 public SnackbarManager(Window window) { | 140 public SnackbarManager(Activity activity) { |
111 mDecor = window.getDecorView(); | 141 mActivity = activity; |
142 mDecor = activity.getWindow().getDecorView(); | |
112 mUIThreadHandler = new Handler(); | 143 mUIThreadHandler = new Handler(); |
113 mIsTablet = DeviceFormFactor.isTablet(mDecor.getContext()); | 144 mIsTablet = DeviceFormFactor.isTablet(mDecor.getContext()); |
114 } | 145 } |
115 | 146 |
116 /** | 147 /** |
117 * Shows a snackbar at the bottom of the screen, or above the keyboard if th e keyboard is | 148 * Shows a snackbar at the bottom of the screen, or above the keyboard if th e keyboard is |
118 * visible. | 149 * visible. |
119 */ | 150 */ |
120 public void showSnackbar(Snackbar snackbar) { | 151 public void showSnackbar(Snackbar snackbar) { |
121 int durationMs = snackbar.getDuration(); | 152 int durationMs = snackbar.getDuration(); |
(...skipping 21 matching lines...) Expand all Loading... | |
143 * Warning: Calling this method might cause cascading destroy loop, because you might trigger | 174 * Warning: Calling this method might cause cascading destroy loop, because you might trigger |
144 * callbacks for other {@link SnackbarController}. This method is only meant to be used during | 175 * callbacks for other {@link SnackbarController}. This method is only meant to be used during |
145 * {@link ChromeActivity}'s destruction routine. For other purposes, use | 176 * {@link ChromeActivity}'s destruction routine. For other purposes, use |
146 * {@link #dismissSnackbars(SnackbarController)} instead. | 177 * {@link #dismissSnackbars(SnackbarController)} instead. |
147 * <p> | 178 * <p> |
148 * Dismisses all snackbars in stack. This will call | 179 * Dismisses all snackbars in stack. This will call |
149 * {@link SnackbarController#onDismissNoAction(Object)} for every closing sn ackbar. | 180 * {@link SnackbarController#onDismissNoAction(Object)} for every closing sn ackbar. |
150 * | 181 * |
151 * @param isTimeout Whether dismissal was triggered by timeout. | 182 * @param isTimeout Whether dismissal was triggered by timeout. |
152 */ | 183 */ |
184 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) | |
153 public void dismissAllSnackbars(boolean isTimeout) { | 185 public void dismissAllSnackbars(boolean isTimeout) { |
154 mUIThreadHandler.removeCallbacks(mHideRunnable); | 186 mUIThreadHandler.removeCallbacks(mHideRunnable); |
155 | 187 |
156 if (mPopup != null) { | 188 if (mPopup != null) { |
157 mPopup.dismiss(); | 189 // TODO(ianwen): remove the try catch after crbug.com/553569 is fixe d. |
190 try { | |
191 mPopup.dismiss(); | |
192 } catch (IllegalArgumentException ex) { | |
193 if (mActivity != null) { | |
194 android.util.Log.d(TAG, "Activity.toString()? " + mActivity) ; | |
195 android.util.Log.d(TAG, "Activity is finishing? " + mActivit y.isFinishing()); | |
196 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_ MR1) { | |
197 android.util.Log.d(TAG, "Activity is destroyed?" + mActi vity.isDestroyed()); | |
198 } | |
199 } | |
200 if (sWindowDetachTrace != null) { | |
201 throw sWindowDetachTrace; | |
newt (away)
2015/11/18 01:46:54
If you throw sWindowDetachTrace then Chrome will c
Ian Wen
2015/11/18 01:56:26
Done.
| |
202 } | |
203 throw ex; | |
204 } | |
205 | |
158 mPopup = null; | 206 mPopup = null; |
159 } | 207 } |
160 | 208 |
161 HashSet<SnackbarController> controllers = new HashSet<SnackbarController >(); | 209 HashSet<SnackbarController> controllers = new HashSet<SnackbarController >(); |
162 | 210 |
163 while (!mStack.isEmpty()) { | 211 while (!mStack.isEmpty()) { |
164 Snackbar snackbar = mStack.pop(); | 212 Snackbar snackbar = mStack.pop(); |
165 if (!controllers.contains(snackbar.getController())) { | 213 if (!controllers.contains(snackbar.getController())) { |
166 snackbar.getController().onDismissForEachType(isTimeout); | 214 snackbar.getController().onDismissForEachType(isTimeout); |
167 controllers.add(snackbar.getController()); | 215 controllers.add(snackbar.getController()); |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
296 /** | 344 /** |
297 * Overrides the default snackbar duration with a custom value for testing. | 345 * Overrides the default snackbar duration with a custom value for testing. |
298 * @param durationMs The duration to use in ms. | 346 * @param durationMs The duration to use in ms. |
299 */ | 347 */ |
300 @VisibleForTesting | 348 @VisibleForTesting |
301 public static void setDurationForTesting(int durationMs) { | 349 public static void setDurationForTesting(int durationMs) { |
302 sSnackbarDurationMs = durationMs; | 350 sSnackbarDurationMs = durationMs; |
303 sAccessibilitySnackbarDurationMs = durationMs; | 351 sAccessibilitySnackbarDurationMs = durationMs; |
304 } | 352 } |
305 } | 353 } |
OLD | NEW |