| 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.graphics.Rect; |
| 7 import android.os.Handler; | 8 import android.os.Handler; |
| 8 import android.view.Gravity; | 9 import android.view.Gravity; |
| 9 import android.view.View; | 10 import android.view.View; |
| 10 import android.view.View.OnClickListener; | 11 import android.view.View.OnClickListener; |
| 11 import android.view.ViewTreeObserver.OnGlobalLayoutListener; | 12 import android.view.ViewTreeObserver.OnGlobalLayoutListener; |
| 13 import android.view.Window; |
| 12 | 14 |
| 13 import org.chromium.base.ApiCompatibilityUtils; | 15 import org.chromium.base.ApiCompatibilityUtils; |
| 14 import org.chromium.base.VisibleForTesting; | 16 import org.chromium.base.VisibleForTesting; |
| 15 import org.chromium.chrome.R; | 17 import org.chromium.chrome.R; |
| 16 import org.chromium.chrome.browser.device.DeviceClassManager; | 18 import org.chromium.chrome.browser.device.DeviceClassManager; |
| 19 import org.chromium.ui.UiUtils; |
| 17 import org.chromium.ui.base.DeviceFormFactor; | 20 import org.chromium.ui.base.DeviceFormFactor; |
| 18 | 21 |
| 19 import java.util.HashSet; | 22 import java.util.HashSet; |
| 20 import java.util.Stack; | 23 import java.util.Stack; |
| 21 | 24 |
| 22 /** | 25 /** |
| 23 * Manager for the snackbar showing at the bottom of activity. | 26 * Manager for the snackbar showing at the bottom of activity. |
| 24 * <p/> | 27 * <p/> |
| 25 * There should be only one SnackbarManager and one snackbar in the activity. Th
e manager maintains | 28 * There should be only one SnackbarManager and one snackbar in the activity. Th
e manager maintains |
| 26 * a stack to store all entries that should be displayed. When showing a new sna
ckbar, old entry | 29 * a stack to store all entries that should be displayed. When showing a new sna
ckbar, old entry |
| 27 * will be pushed to stack and text/button will be updated to the newest entry. | 30 * will be pushed to stack and text/button will be updated to the newest entry. |
| 28 * <p/> | 31 * <p/> |
| 29 * When action button is clicked, this manager will call | 32 * When action button is clicked, this manager will call |
| 30 * {@link SnackbarController#onAction(Object)} in corresponding listener, and sh
ow the next | 33 * {@link SnackbarController#onAction(Object)} in corresponding listener, and sh
ow the next |
| 31 * entry in stack. Otherwise if no action is taken by user during | 34 * entry in stack. Otherwise if no action is taken by user during |
| 32 * {@link #DEFAULT_SNACKBAR_SHOW_DURATION_MS} milliseconds, it will clear the st
ack and call | 35 * {@link #DEFAULT_SNACKBAR_DURATION_MS} milliseconds, it will clear the stack a
nd call |
| 33 * {@link SnackbarController#onDismissNoAction(Object)} to all listeners. | 36 * {@link SnackbarController#onDismissNoAction(Object)} to all listeners. |
| 34 */ | 37 */ |
| 35 public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
{ | 38 public class SnackbarManager implements OnClickListener, OnGlobalLayoutListener
{ |
| 36 | 39 |
| 37 /** | 40 /** |
| 38 * Interface that shows the ability to provide a unified snackbar manager. | 41 * Interface that shows the ability to provide a unified snackbar manager. |
| 39 */ | 42 */ |
| 40 public interface SnackbarManageable { | 43 public interface SnackbarManageable { |
| 41 /** | 44 /** |
| 42 * @return The snackbar manager that has a proper anchor view. | 45 * @return The snackbar manager that has a proper anchor view. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 69 | 72 |
| 70 /** | 73 /** |
| 71 * Notify each SnackbarControllers instance only once immediately before
the snackbar is | 74 * Notify each SnackbarControllers instance only once immediately before
the snackbar is |
| 72 * dismissed. This function is likely to be used for controllers to do u
ser metrics for | 75 * dismissed. This function is likely to be used for controllers to do u
ser metrics for |
| 73 * dismissal. | 76 * dismissal. |
| 74 * @param isTimeout Whether this dismissal is triggered by timeout. | 77 * @param isTimeout Whether this dismissal is triggered by timeout. |
| 75 */ | 78 */ |
| 76 void onDismissForEachType(boolean isTimeout); | 79 void onDismissForEachType(boolean isTimeout); |
| 77 } | 80 } |
| 78 | 81 |
| 79 private static final int DEFAULT_SNACKBAR_SHOW_DURATION_MS = 3000; | 82 private static final int DEFAULT_SNACKBAR_DURATION_MS = 3000; |
| 80 private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000; | 83 private static final int ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS = 6000; |
| 81 | 84 |
| 82 // Used instead of the constant so tests can override the value. | 85 // Used instead of the constant so tests can override the value. |
| 83 private static int sUndoBarShowDurationMs = DEFAULT_SNACKBAR_SHOW_DURATION_M
S; | 86 private static int sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS; |
| 84 private static int sAccessibilityUndoBarDurationMs = ACCESSIBILITY_MODE_SNAC
KBAR_DURATION_MS; | 87 private static int sAccessibilitySnackbarDurationMs = ACCESSIBILITY_MODE_SNA
CKBAR_DURATION_MS; |
| 85 | 88 |
| 86 private final boolean mIsTablet; | 89 private final boolean mIsTablet; |
| 87 | 90 |
| 88 private View mParent; | 91 private View mDecor; |
| 89 // Variable storing current xy position of parent view. | |
| 90 private int[] mTempTopLeft = new int[2]; | |
| 91 private final Handler mUIThreadHandler; | 92 private final Handler mUIThreadHandler; |
| 92 private Stack<SnackbarEntry> mStack = new Stack<SnackbarEntry>(); | 93 private Stack<Snackbar> mStack = new Stack<Snackbar>(); |
| 93 private SnackbarPopupWindow mPopup; | 94 private SnackbarPopupWindow mPopup; |
| 94 private final Runnable mHideRunnable = new Runnable() { | 95 private final Runnable mHideRunnable = new Runnable() { |
| 95 @Override | 96 @Override |
| 96 public void run() { | 97 public void run() { |
| 97 dismissSnackbar(true); | 98 dismissSnackbar(true); |
| 98 } | 99 } |
| 99 }; | 100 }; |
| 100 | 101 |
| 102 // Variables used and reused in local calculations. |
| 103 private int[] mTempDecorPosition = new int[2]; |
| 104 private Rect mTempVisibleDisplayFrame = new Rect(); |
| 105 |
| 101 /** | 106 /** |
| 102 * Create an instance of SnackbarManager with the root view of entire activi
ty. | 107 * Constructs a SnackbarManager to show snackbars in the given window. |
| 103 * @param parent The view that snackbar anchors to. Since SnackbarManager sh
ould be initialized | |
| 104 * during activity initialization, parent should always be set
to root view of | |
| 105 * entire activity. | |
| 106 */ | 108 */ |
| 107 public SnackbarManager(View parent) { | 109 public SnackbarManager(Window window) { |
| 108 mParent = parent; | 110 mDecor = window.getDecorView(); |
| 109 mUIThreadHandler = new Handler(); | 111 mUIThreadHandler = new Handler(); |
| 110 mIsTablet = DeviceFormFactor.isTablet(parent.getContext()); | 112 mIsTablet = DeviceFormFactor.isTablet(mDecor.getContext()); |
| 111 } | 113 } |
| 112 | 114 |
| 113 /** | 115 /** |
| 116 * Shows a snackbar at the bottom of the screen, or above the keyboard if th
e keyboard is |
| 117 * visible. |
| 118 */ |
| 119 public void showSnackbar(Snackbar snackbar) { |
| 120 int durationMs = snackbar.getDuration(); |
| 121 if (durationMs == 0) { |
| 122 durationMs = DeviceClassManager.isAccessibilityModeEnabled(mDecor.ge
tContext()) |
| 123 ? sAccessibilitySnackbarDurationMs : sSnackbarDurationMs; |
| 124 } |
| 125 |
| 126 mUIThreadHandler.removeCallbacks(mHideRunnable); |
| 127 mUIThreadHandler.postDelayed(mHideRunnable, durationMs); |
| 128 |
| 129 mStack.push(snackbar); |
| 130 if (mPopup == null) { |
| 131 mPopup = new SnackbarPopupWindow(mDecor, this, snackbar); |
| 132 showPopupAtBottom(); |
| 133 mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this); |
| 134 } else { |
| 135 mPopup.update(snackbar, true); |
| 136 } |
| 137 |
| 138 mPopup.announceforAccessibility(); |
| 139 } |
| 140 |
| 141 /** |
| 142 * TODO(newt): delete this method. Update callers to use {@link #showSnackba
r(Snackbar)}. |
| 114 * Shows a snackbar with description text and an action button. | 143 * Shows a snackbar with description text and an action button. |
| 115 * @param template Teamplate used to compose full description. | 144 * @param template Teamplate used to compose full description. |
| 116 * @param description Text for description showing at start of snackbar. | 145 * @param description Text for description showing at start of snackbar. |
| 117 * @param actionText Text for action button to show. | 146 * @param actionText Text for action button to show. |
| 118 * @param actionData Data bound to this snackbar entry. Will be returned to
listeners when | 147 * @param actionData Data bound to this snackbar entry. Will be returned to
listeners when |
| 119 * action be clicked or snackbar be dismissed. | 148 * action be clicked or snackbar be dismissed. |
| 120 * @param controller Listener for this snackbar entry. | 149 * @param controller Listener for this snackbar entry. |
| 121 */ | 150 */ |
| 122 public void showSnackbar(String template, String description, String actionT
ext, | 151 public void showSnackbar(String template, String description, String actionT
ext, |
| 123 Object actionData, SnackbarController controller) { | 152 Object actionData, SnackbarController controller) { |
| 124 int duration = sUndoBarShowDurationMs; | 153 showSnackbar(Snackbar.make(description, controller).setTemplateText(temp
late) |
| 125 // Duration for snackbars to show is different in normal mode and in acc
essibility mode. | 154 .setAction(actionText, actionData)); |
| 126 if (DeviceClassManager.isAccessibilityModeEnabled(mParent.getContext()))
{ | |
| 127 duration = sAccessibilityUndoBarDurationMs; | |
| 128 } | |
| 129 showSnackbar(template, description, actionText, actionData, controller,
duration); | |
| 130 } | 155 } |
| 131 | 156 |
| 132 /** | 157 /** |
| 158 * TODO(newt): delete this method. Update callers to use {@link #showSnackba
r(Snackbar)}. |
| 133 * Shows a snackbar for the given timeout duration with description text and
an action button. | 159 * Shows a snackbar for the given timeout duration with description text and
an action button. |
| 134 * Allows overriding the default timeout of {@link #DEFAULT_SNACKBAR_SHOW_DU
RATION_MS} with | 160 * Allows overriding the default timeout of {@link #DEFAULT_SNACKBAR_DURATIO
N_MS} with |
| 135 * a custom value. | 161 * a custom value. |
| 136 * @param template Teamplate used to compose full description. | 162 * @param template Teamplate used to compose full description. |
| 137 * @param description Text for description showing at start of snackbar. | 163 * @param description Text for description showing at start of snackbar. |
| 138 * @param actionText Text for action button to show. | 164 * @param actionText Text for action button to show. |
| 139 * @param actionData Data bound to this snackbar entry. Will be returned to
listeners when | 165 * @param actionData Data bound to this snackbar entry. Will be returned to
listeners when |
| 140 * action be clicked or snackbar be dismissed. | 166 * action be clicked or snackbar be dismissed. |
| 141 * @param controller Listener for this snackbar entry. | 167 * @param controller Listener for this snackbar entry. |
| 142 * @param timeoutMs The timeout to use in ms. | 168 * @param timeoutMs The timeout to use in ms. |
| 143 */ | 169 */ |
| 144 public void showSnackbar(String template, String description, String actionT
ext, | 170 public void showSnackbar(String template, String description, String actionT
ext, |
| 145 Object actionData, SnackbarController controller, int timeoutMs) { | 171 Object actionData, SnackbarController controller, int timeoutMs) { |
| 146 mUIThreadHandler.removeCallbacks(mHideRunnable); | 172 showSnackbar(Snackbar.make(description, controller).setTemplateText(temp
late) |
| 147 mUIThreadHandler.postDelayed(mHideRunnable, timeoutMs); | 173 .setAction(actionText, actionData).setDuration(timeoutMs)); |
| 148 | |
| 149 mStack.push(new SnackbarEntry(template, description, actionText, actionD
ata, controller)); | |
| 150 if (mPopup == null) { | |
| 151 mPopup = new SnackbarPopupWindow(mParent, this, template, descriptio
n, actionText); | |
| 152 showPopupAtBottom(); | |
| 153 mParent.getViewTreeObserver().addOnGlobalLayoutListener(this); | |
| 154 } else { | |
| 155 mPopup.setTextViews(template, description, actionText, true); | |
| 156 } | |
| 157 | |
| 158 mPopup.announceforAccessibility(); | |
| 159 } | 174 } |
| 160 | 175 |
| 161 /** | 176 /** |
| 162 * Convinient function for showSnackbar. Note this method adds passed entry
to stack. | |
| 163 */ | |
| 164 private void showSnackbar(SnackbarEntry entry) { | |
| 165 showSnackbar(entry.mTemplate, entry.mDescription, entry.mActionText, ent
ry.mData, | |
| 166 entry.mController); | |
| 167 } | |
| 168 | |
| 169 /** | |
| 170 * Change parent view of snackbar. This method is likely to be called when a
new window is | |
| 171 * hiding the snackbar and will dismiss all snackbars. | |
| 172 * @param newParent The new parent view snackbar anchors to. | |
| 173 */ | |
| 174 public void setParentView(View newParent) { | |
| 175 if (newParent == mParent) return; | |
| 176 mParent.getViewTreeObserver().removeOnGlobalLayoutListener(this); | |
| 177 mUIThreadHandler.removeCallbacks(mHideRunnable); | |
| 178 dismissSnackbar(false); | |
| 179 mParent = newParent; | |
| 180 } | |
| 181 | |
| 182 /** | |
| 183 * Dismisses snackbar, clears out all entries in stack and prevents future r
emove callbacks from | 177 * Dismisses snackbar, clears out all entries in stack and prevents future r
emove callbacks from |
| 184 * happening. This method also unregisters this class from global layout not
ifications. | 178 * happening. This method also unregisters this class from global layout not
ifications. |
| 185 * @param isTimeout Whether dismissal was triggered by timeout. | 179 * @param isTimeout Whether dismissal was triggered by timeout. |
| 186 */ | 180 */ |
| 187 public void dismissSnackbar(boolean isTimeout) { | 181 public void dismissSnackbar(boolean isTimeout) { |
| 188 mUIThreadHandler.removeCallbacks(mHideRunnable); | 182 mUIThreadHandler.removeCallbacks(mHideRunnable); |
| 189 | 183 |
| 190 if (mPopup != null) { | 184 if (mPopup != null) { |
| 191 mPopup.dismiss(); | 185 mPopup.dismiss(); |
| 192 mPopup = null; | 186 mPopup = null; |
| 193 } | 187 } |
| 194 | 188 |
| 195 HashSet<SnackbarController> controllers = new HashSet<SnackbarController
>(); | 189 HashSet<SnackbarController> controllers = new HashSet<SnackbarController
>(); |
| 196 | 190 |
| 197 while (!mStack.isEmpty()) { | 191 while (!mStack.isEmpty()) { |
| 198 SnackbarEntry entry = mStack.pop(); | 192 Snackbar snackbar = mStack.pop(); |
| 199 if (!controllers.contains(entry.mController)) { | 193 if (!controllers.contains(snackbar.getController())) { |
| 200 entry.mController.onDismissForEachType(isTimeout); | 194 snackbar.getController().onDismissForEachType(isTimeout); |
| 201 controllers.add(entry.mController); | 195 controllers.add(snackbar.getController()); |
| 202 } | 196 } |
| 203 entry.mController.onDismissNoAction(entry.mData); | 197 snackbar.getController().onDismissNoAction(snackbar.getActionData())
; |
| 204 } | 198 } |
| 205 mParent.getViewTreeObserver().removeOnGlobalLayoutListener(this); | 199 mDecor.getViewTreeObserver().removeOnGlobalLayoutListener(this); |
| 206 } | 200 } |
| 207 | 201 |
| 208 /** | 202 /** |
| 209 * Removes all entries for certain type of controller. This method is used w
hen a controller | 203 * Removes all entries for certain type of controller. This method is used w
hen a controller |
| 210 * wants to remove all entries it posted to snackbar manager before. | 204 * wants to remove all entries it posted to snackbar manager before. |
| 211 * @param controller This method only removes entries posted by this control
ler. | 205 * @param controller This method only removes entries posted by this control
ler. |
| 212 */ | 206 */ |
| 213 public void removeSnackbarEntry(SnackbarController controller) { | 207 public void removeSnackbarEntry(SnackbarController controller) { |
| 214 boolean isFound = false; | 208 boolean isFound = false; |
| 215 SnackbarEntry[] snackbarEntries = new SnackbarEntry[mStack.size()]; | 209 Snackbar[] snackbars = new Snackbar[mStack.size()]; |
| 216 mStack.toArray(snackbarEntries); | 210 mStack.toArray(snackbars); |
| 217 for (SnackbarEntry entry : snackbarEntries) { | 211 for (Snackbar snackbar : snackbars) { |
| 218 if (entry.mController == controller) { | 212 if (snackbar.getController() == controller) { |
| 219 mStack.remove(entry); | 213 mStack.remove(snackbar); |
| 220 isFound = true; | 214 isFound = true; |
| 221 } | 215 } |
| 222 } | 216 } |
| 223 if (!isFound) return; | 217 if (!isFound) return; |
| 224 | 218 |
| 225 finishSnackbarEntryRemoval(controller); | 219 finishSnackbarEntryRemoval(controller); |
| 226 } | 220 } |
| 227 | 221 |
| 228 /** | 222 /** |
| 229 * Removes all entries for certain type of controller and with specified dat
a. This method is | 223 * Removes all entries for certain type of controller and with specified dat
a. This method is |
| 230 * used when a controller wants to remove some entries it posted to snackbar
manager before. | 224 * used when a controller wants to remove some entries it posted to snackbar
manager before. |
| 231 * However it does not affect other controllers' entries. Note that this met
hod assumes | 225 * However it does not affect other controllers' entries. Note that this met
hod assumes |
| 232 * different types of snackbar controllers are not sharing the same instance
. | 226 * different types of snackbar controllers are not sharing the same instance
. |
| 233 * @param controller This method only removes entries posted by this control
ler. | 227 * @param controller This method only removes entries posted by this control
ler. |
| 234 * @param data Identifier of an entry to be removed from stack. | 228 * @param data Identifier of an entry to be removed from stack. |
| 235 */ | 229 */ |
| 236 public void removeSnackbarEntry(SnackbarController controller, Object data)
{ | 230 public void removeSnackbarEntry(SnackbarController controller, Object data)
{ |
| 237 boolean isFound = false; | 231 boolean isFound = false; |
| 238 for (SnackbarEntry entry : mStack) { | 232 for (Snackbar snackbar : mStack) { |
| 239 if (entry.mData != null && entry.mData.equals(data) | 233 if (snackbar.getActionData() != null && snackbar.getActionData().equ
als(data) |
| 240 && entry.mController == controller) { | 234 && snackbar.getController() == controller) { |
| 241 mStack.remove(entry); | 235 mStack.remove(snackbar); |
| 242 isFound = true; | 236 isFound = true; |
| 243 break; | 237 break; |
| 244 } | 238 } |
| 245 } | 239 } |
| 246 if (!isFound) return; | 240 if (!isFound) return; |
| 247 | 241 |
| 248 finishSnackbarEntryRemoval(controller); | 242 finishSnackbarEntryRemoval(controller); |
| 249 } | 243 } |
| 250 | 244 |
| 251 private void finishSnackbarEntryRemoval(SnackbarController controller) { | 245 private void finishSnackbarEntryRemoval(SnackbarController controller) { |
| 252 controller.onDismissForEachType(false); | 246 controller.onDismissForEachType(false); |
| 253 | 247 |
| 254 if (mStack.isEmpty()) { | 248 if (mStack.isEmpty()) { |
| 255 dismissSnackbar(false); | 249 dismissSnackbar(false); |
| 256 } else { | 250 } else { |
| 257 // Refresh the snackbar to let it show top of stack and have full ti
meout. | 251 // Refresh the snackbar to let it show top of stack and have full ti
meout. |
| 258 showSnackbar(mStack.pop()); | 252 showSnackbar(mStack.pop()); |
| 259 } | 253 } |
| 260 } | 254 } |
| 261 | 255 |
| 262 /** | 256 /** |
| 263 * Handles click event for action button at end of snackbar. | 257 * Handles click event for action button at end of snackbar. |
| 264 */ | 258 */ |
| 265 @Override | 259 @Override |
| 266 public void onClick(View v) { | 260 public void onClick(View v) { |
| 267 assert !mStack.isEmpty(); | 261 assert !mStack.isEmpty(); |
| 268 | 262 |
| 269 SnackbarEntry entry = mStack.pop(); | 263 Snackbar snackbar = mStack.pop(); |
| 270 entry.mController.onAction(entry.mData); | 264 snackbar.getController().onAction(snackbar.getActionData()); |
| 271 | 265 |
| 272 if (!mStack.isEmpty()) { | 266 if (!mStack.isEmpty()) { |
| 273 showSnackbar(mStack.pop()); | 267 showSnackbar(mStack.pop()); |
| 274 } else { | 268 } else { |
| 275 dismissSnackbar(false); | 269 dismissSnackbar(false); |
| 276 } | 270 } |
| 277 } | 271 } |
| 278 | 272 |
| 279 /** | |
| 280 * Calculates the show-up position from TOP START corner of parent view as a
workaround of an | |
| 281 * android bug http://b/17789629 on Lollipop. | |
| 282 */ | |
| 283 private void showPopupAtBottom() { | 273 private void showPopupAtBottom() { |
| 284 int margin = mIsTablet ? mParent.getResources().getDimensionPixelSize( | 274 // When the keyboard is showing, translating the snackbar upwards looks
bad because it |
| 285 R.dimen.undo_bar_tablet_margin) : 0; | 275 // overlaps the keyboard. In this case, use an alternative animation wit
hout translation. |
| 286 mParent.getLocationInWindow(mTempTopLeft); | 276 boolean isKeyboardShowing = UiUtils.isKeyboardShowing(mDecor.getContext(
), mDecor); |
| 287 mPopup.showAtLocation(mParent, Gravity.START | Gravity.TOP, margin, | 277 mPopup.setAnimationStyle(isKeyboardShowing ? R.style.SnackbarAnimationWi
thKeyboard |
| 288 mTempTopLeft[1] + mParent.getHeight() - mPopup.getHeight() - mar
gin); | 278 : R.style.SnackbarAnimation); |
| 279 |
| 280 mDecor.getLocationInWindow(mTempDecorPosition); |
| 281 mDecor.getWindowVisibleDisplayFrame(mTempVisibleDisplayFrame); |
| 282 int decorBottom = mTempDecorPosition[1] + mDecor.getHeight(); |
| 283 int visibleBottom = Math.min(mTempVisibleDisplayFrame.bottom, decorBotto
m); |
| 284 int margin = mIsTablet ? mDecor.getResources().getDimensionPixelSize( |
| 285 R.dimen.snackbar_tablet_margin) : 0; |
| 286 |
| 287 mPopup.showAtLocation(mDecor, Gravity.START | Gravity.BOTTOM, margin, |
| 288 decorBottom - visibleBottom + margin); |
| 289 } | 289 } |
| 290 | 290 |
| 291 /** | 291 /** |
| 292 * Resize and re-align popup window when device orientation changes, or soft
keyboard shows up. | 292 * Resize and re-position popup window when the device orientation changes o
r the software |
| 293 * keyboard appears. Be careful not to let the snackbar overlap the Android
navigation bar: |
| 294 * http://b/17789629. |
| 293 */ | 295 */ |
| 294 @Override | 296 @Override |
| 295 public void onGlobalLayout() { | 297 public void onGlobalLayout() { |
| 296 if (mPopup == null) return; | 298 if (mPopup == null) return; |
| 297 mParent.getLocationInWindow(mTempTopLeft); | 299 |
| 300 mDecor.getLocationInWindow(mTempDecorPosition); |
| 301 mDecor.getWindowVisibleDisplayFrame(mTempVisibleDisplayFrame); |
| 302 int decorBottom = mTempDecorPosition[1] + mDecor.getHeight(); |
| 303 int visibleBottom = Math.min(mTempVisibleDisplayFrame.bottom, decorBotto
m); |
| 304 |
| 298 if (mIsTablet) { | 305 if (mIsTablet) { |
| 299 int margin = mParent.getResources().getDimensionPixelSize( | 306 int margin = mDecor.getResources().getDimensionPixelOffset( |
| 300 R.dimen.undo_bar_tablet_margin); | 307 R.dimen.snackbar_tablet_margin); |
| 301 int width = mParent.getResources().getDimensionPixelSize( | 308 int width = mDecor.getResources().getDimensionPixelSize(R.dimen.snac
kbar_tablet_width); |
| 302 R.dimen.undo_bar_tablet_width); | 309 boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(mDecor); |
| 303 boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(mParent); | 310 int startPosition = isRtl ? mDecor.getRight() - width - margin |
| 304 int startPosition = isRtl ? mParent.getRight() - width - margin | 311 : mDecor.getLeft() + margin; |
| 305 : mParent.getLeft() + margin; | 312 mPopup.update(startPosition, decorBottom - visibleBottom + margin, w
idth, -1); |
| 306 mPopup.update(startPosition, | |
| 307 mTempTopLeft[1] + mParent.getHeight() - mPopup.getHeight() -
margin, width, -1); | |
| 308 } else { | 313 } else { |
| 309 // Phone relayout | 314 mPopup.update(mDecor.getLeft(), decorBottom - visibleBottom, mDecor.
getWidth(), -1); |
| 310 mPopup.update(mParent.getLeft(), | |
| 311 mTempTopLeft[1] + mParent.getHeight() - mPopup.getHeight(),
mParent.getWidth(), | |
| 312 -1); | |
| 313 } | 315 } |
| 314 } | 316 } |
| 315 | 317 |
| 316 /** | 318 /** |
| 317 * @return Whether there is a snackbar on screen. | 319 * @return Whether there is a snackbar on screen. |
| 318 */ | 320 */ |
| 319 public boolean isShowing() { | 321 public boolean isShowing() { |
| 320 if (mPopup == null) return false; | 322 if (mPopup == null) return false; |
| 321 return mPopup.isShowing(); | 323 return mPopup.isShowing(); |
| 322 } | 324 } |
| 323 | 325 |
| 324 /** | 326 /** |
| 325 * Allows overriding the default timeout of {@link #DEFAULT_SNACKBAR_SHOW_DU
RATION_MS} with | 327 * Overrides the default snackbar duration with a custom value for testing. |
| 326 * a custom value. This is meant to be used by tests. | 328 * @param durationMs The duration to use in ms. |
| 327 * @param timeoutMs The new timeout to use in ms. | |
| 328 */ | 329 */ |
| 329 @VisibleForTesting | 330 @VisibleForTesting |
| 330 public static void setTimeoutForTesting(int timeoutMs) { | 331 public static void setDurationForTesting(int durationMs) { |
| 331 sUndoBarShowDurationMs = timeoutMs; | 332 sSnackbarDurationMs = durationMs; |
| 332 sAccessibilityUndoBarDurationMs = timeoutMs; | 333 sAccessibilitySnackbarDurationMs = durationMs; |
| 333 } | |
| 334 | |
| 335 /** | |
| 336 * Simple data structure representing a single snackbar in stack. | |
| 337 */ | |
| 338 private static class SnackbarEntry { | |
| 339 public String mTemplate; | |
| 340 public String mDescription; | |
| 341 public String mActionText; | |
| 342 public Object mData; | |
| 343 public SnackbarController mController; | |
| 344 | |
| 345 public SnackbarEntry(String template, String description, String actionT
ext, | |
| 346 Object actionData, SnackbarController controller) { | |
| 347 mTemplate = template; | |
| 348 mDescription = description; | |
| 349 mActionText = actionText; | |
| 350 mData = actionData; | |
| 351 mController = controller; | |
| 352 } | |
| 353 } | 334 } |
| 354 } | 335 } |
| OLD | NEW |