blob: a907211b6f637a43b3d69c596665a1354b440036 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.snackbar;
import android.text.TextUtils;
import org.chromium.chrome.browser.snackbar.SnackbarManager.SnackbarController;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
/**
* A data structure that holds all the {@link Snackbar}s managed by {@link SnackbarManager}.
*/
class SnackbarCollection {
private Deque<Snackbar> mSnackbars = new LinkedList<>();
private Deque<Snackbar> mPersistentSnackbars = new LinkedList<>();
/**
* Adds a new snackbar to the collection. If the new snackbar is of
* {@link Snackbar#TYPE_ACTION} and current snackbar is of
* {@link Snackbar#TYPE_NOTIFICATION}, the current snackbar will be removed from the
* collection immediately. Snackbars of {@link Snackbar#TYPE_PERSISTENT} will appear after all
* action and notification type snackbars are dismissed and will be hidden if an action or
* notifications are added after it.
*/
void add(Snackbar snackbar) {
if (snackbar.isTypeAction()) {
if (getCurrent() != null && !getCurrent().isTypeAction()) {
removeCurrent(false);
}
mSnackbars.addFirst(snackbar);
} else if (snackbar.isTypePersistent()) {
// Although persistent snackbars set their action text by default, it is possible that
// the developer overrides it. This is a safeguard to ensure all persistent snackbars
// have a method of dismissal.
assert !TextUtils.isEmpty(snackbar.getActionText())
: "Persistent snackbars require action text.";
mPersistentSnackbars.addFirst(snackbar);
} else {
mSnackbars.addLast(snackbar);
}
}
/**
* Removes the current snackbar from the collection after the user has clicked on the action
* button.
*/
Snackbar removeCurrentDueToAction() {
return removeCurrent(true);
}
private Snackbar removeCurrent(boolean isAction) {
Snackbar current = mSnackbars.pollFirst();
if (current == null) {
current = mPersistentSnackbars.pollFirst();
}
if (current != null) {
SnackbarController controller = current.getController();
if (isAction) controller.onAction(current.getActionData());
else controller.onDismissNoAction(current.getActionData());
}
return current;
}
/**
* @return The snackbar that is currently displayed.
*/
Snackbar getCurrent() {
Snackbar current = mSnackbars.peekFirst();
if (current == null) {
current = mPersistentSnackbars.peekFirst();
}
return current;
}
boolean isEmpty() {
return mSnackbars.isEmpty() && mPersistentSnackbars.isEmpty();
}
void clear() {
while (!isEmpty()) {
removeCurrent(false);
}
}
void removeCurrentDueToTimeout() {
Snackbar current = getCurrent();
if (current.isTypePersistent()) {
// In theory, this method should never be called on a persistent snackbar as the
// dismissal handler is disabled. As a precaution, we exit early if the snackbar is
// meant to be persistent.
return;
}
removeCurrent(false);
while ((current = getCurrent()) != null && current.isTypeAction()) {
removeCurrent(false);
}
}
boolean removeMatchingSnackbars(SnackbarController controller) {
return removeSnackbarFromList(mSnackbars, controller)
|| removeSnackbarFromList(mPersistentSnackbars, controller);
}
private static boolean removeSnackbarFromList(Deque<Snackbar> list,
SnackbarController controller) {
boolean snackbarRemoved = false;
Iterator<Snackbar> iter = list.iterator();
while (iter.hasNext()) {
Snackbar snackbar = iter.next();
if (snackbar.getController() != controller) continue;
iter.remove();
controller.onDismissNoAction(snackbar.getActionData());
snackbarRemoved = true;
}
return snackbarRemoved;
}
boolean removeMatchingSnackbars(SnackbarController controller, Object data) {
return removeSnackbarFromList(mSnackbars, controller, data)
|| removeSnackbarFromList(mPersistentSnackbars, controller, data);
}
private static boolean removeSnackbarFromList(Deque<Snackbar> list,
SnackbarController controller, Object data) {
boolean snackbarRemoved = false;
Iterator<Snackbar> iter = list.iterator();
while (iter.hasNext()) {
Snackbar snackbar = iter.next();
if (snackbar.getController() != controller) continue;
if (!objectsAreEqual(snackbar.getActionData(), data)) continue;
iter.remove();
controller.onDismissNoAction(snackbar.getActionData());
snackbarRemoved = true;
}
return snackbarRemoved;
}
private static boolean objectsAreEqual(Object a, Object b) {
if (a == null && b == null) return true;
if (a == null || b == null) return false;
return a.equals(b);
}
}