OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.snackbar.undo; |
| 6 |
| 7 import android.content.Context; |
| 8 |
| 9 import com.google.android.apps.chrome.R; |
| 10 |
| 11 import org.chromium.base.metrics.RecordHistogram; |
| 12 import org.chromium.chrome.browser.Tab; |
| 13 import org.chromium.chrome.browser.device.DeviceClassManager; |
| 14 import org.chromium.chrome.browser.snackbar.SnackbarManager; |
| 15 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| 16 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 17 import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| 18 import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| 19 |
| 20 import java.util.List; |
| 21 import java.util.Locale; |
| 22 |
| 23 /** |
| 24 * A controller that listens to and visually represents cancelable tab closures. |
| 25 * <p/> |
| 26 * Each time a tab is undoably closed via {@link TabModelObserver#tabPendingClos
ure(Tab)}, |
| 27 * this controller saves that tab id and title to the stack of SnackbarManager.
It will then let |
| 28 * SnackbarManager to show a snackbar representing the top entry in of stack. Ea
ch added entry |
| 29 * resets the timeout that tracks when to commit the undoable actions. |
| 30 * <p/> |
| 31 * When the undo button is clicked, it will cancel the tab closure if any. all p
ending closing will |
| 32 * be committed. |
| 33 * <p/> |
| 34 * This class also responds to external changes to the undo state by monitoring |
| 35 * {@link TabModelObserver#tabClosureUndone(Tab)} and |
| 36 * {@link TabModelObserver#tabClosureCommitted(Tab)} to properly keep it's inter
nal state |
| 37 * in sync with the model. |
| 38 */ |
| 39 public class UndoBarPopupController implements SnackbarManager.SnackbarControlle
r { |
| 40 // AndroidTabCloseUndoToastEvent defined in tools/metrics/histograms/histogr
ams.xml. |
| 41 private static final int TAB_CLOSE_UNDO_TOAST_SHOWN_COLD = 0; |
| 42 private static final int TAB_CLOSE_UNDO_TOAST_SHOWN_WARM = 1; |
| 43 private static final int TAB_CLOSE_UNDO_TOAST_PRESSED = 2; |
| 44 private static final int TAB_CLOSE_UNDO_TOAST_DISMISSED_TIMEOUT = 3; |
| 45 private static final int TAB_CLOSE_UNDO_TOAST_DISMISSED_ACTION = 4; |
| 46 private static final int TAB_CLOSE_UNDO_TOAST_COUNT = 5; |
| 47 |
| 48 private final TabModelSelector mTabModelSelector; |
| 49 private final TabModelObserver mTabModelObserver; |
| 50 private final SnackbarManager mSnackbarManager; |
| 51 private final Context mContext; |
| 52 |
| 53 /** |
| 54 * Creates an instance of a {@link UndoBarPopupController}. |
| 55 * @param context The {@link Context} in which snackbar is shown. |
| 56 * @param selector The {@link TabModelSelector} that will be used to commit
and undo tab |
| 57 * closures. |
| 58 * @param snackbarManager The manager that helps to show up snackbar. |
| 59 */ |
| 60 public UndoBarPopupController(Context context, TabModelSelector selector, |
| 61 SnackbarManager snackbarManager) { |
| 62 mSnackbarManager = snackbarManager; |
| 63 mTabModelSelector = selector; |
| 64 mContext = context; |
| 65 mTabModelObserver = new EmptyTabModelObserver() { |
| 66 private boolean disableUndo() { |
| 67 return !DeviceClassManager.enableUndo(mContext) |
| 68 || DeviceClassManager.isAccessibilityModeEnabled(mContex
t) |
| 69 || DeviceClassManager.enableAccessibilityLayout(); |
| 70 } |
| 71 |
| 72 @Override |
| 73 public void tabPendingClosure(Tab tab) { |
| 74 if (disableUndo()) return; |
| 75 showUndoBar(tab.getId(), tab.getTitle()); |
| 76 } |
| 77 |
| 78 @Override |
| 79 public void tabClosureUndone(Tab tab) { |
| 80 if (disableUndo()) return; |
| 81 mSnackbarManager.removeSnackbarEntry(UndoBarPopupController.this
, tab.getId()); |
| 82 } |
| 83 |
| 84 @Override |
| 85 public void tabClosureCommitted(Tab tab) { |
| 86 if (disableUndo()) return; |
| 87 mSnackbarManager.removeSnackbarEntry(UndoBarPopupController.this
, tab.getId()); |
| 88 } |
| 89 |
| 90 @Override |
| 91 public void allTabsPendingClosure(List<Integer> tabIds) { |
| 92 if (disableUndo()) return; |
| 93 showUndoCloseAllBar(tabIds); |
| 94 } |
| 95 |
| 96 @Override |
| 97 public void allTabsClosureCommitted() { |
| 98 if (disableUndo()) return; |
| 99 mSnackbarManager.removeSnackbarEntry(UndoBarPopupController.this
); |
| 100 } |
| 101 }; |
| 102 } |
| 103 |
| 104 /** |
| 105 * Carry out native library dependent operations like registering observers
and notifications. |
| 106 */ |
| 107 public void initialize() { |
| 108 mTabModelSelector.getModel(false).addObserver(mTabModelObserver); |
| 109 } |
| 110 |
| 111 /** |
| 112 * Cleans up this class, unregistering for application notifications from th
e |
| 113 * {@link TabModelSelector}. |
| 114 */ |
| 115 public void destroy() { |
| 116 TabModel model = mTabModelSelector.getModel(false); |
| 117 if (model != null) model.removeObserver(mTabModelObserver); |
| 118 } |
| 119 |
| 120 /** |
| 121 * Shows an undo bar. Based on user actions, this will cause a call to eithe
r |
| 122 * {@link TabModel#commitTabClosure(int)} or {@link TabModel#cancelTabClosur
e(int)} to be called |
| 123 * for {@code tabId}. |
| 124 * |
| 125 * @param tabId The id of the tab. |
| 126 * @param content The title of the tab. |
| 127 */ |
| 128 private void showUndoBar(int tabId, String content) { |
| 129 RecordHistogram.recordEnumeratedHistogram("AndroidTabCloseUndo.Toast", |
| 130 mSnackbarManager.isShowing() ? TAB_CLOSE_UNDO_TOAST_SHOWN_WARM |
| 131 : TAB_CLOSE_UNDO_TOAST_SHOWN_COLD, |
| 132 TAB_CLOSE_UNDO_TOAST_COUNT); |
| 133 mSnackbarManager.showSnackbar(mContext.getString(R.string.undo_bar_close
_message), |
| 134 content, mContext.getString(R.string.undo_bar_button_text), |
| 135 tabId, this); |
| 136 } |
| 137 |
| 138 /** |
| 139 * Shows an undo close all bar. Based on user actions, this will cause a cal
l to either |
| 140 * {@link TabModel#commitTabClosure(int)} or {@link TabModel#cancelTabClosur
e(int)} to be called |
| 141 * for each tab in {@code closedTabIds}. This will happen unless |
| 142 * {@code SnackbarManager#removeFromStackForData(Object)} is called. |
| 143 * |
| 144 * @param closedTabIds A list of ids corresponding to tabs that were closed |
| 145 */ |
| 146 private void showUndoCloseAllBar(List<Integer> closedTabIds) { |
| 147 String content = String.format(Locale.getDefault(), "%d", closedTabIds.s
ize()); |
| 148 mSnackbarManager.showSnackbar(mContext.getString(R.string.undo_bar_close
_all_message), |
| 149 content, mContext.getString(R.string.undo_bar_button_text), |
| 150 closedTabIds, this); |
| 151 } |
| 152 |
| 153 /** |
| 154 * Calls {@link TabModel#cancelTabClosure(int)} for the tab or for each tab
in |
| 155 * the list of closed tabs. |
| 156 */ |
| 157 @SuppressWarnings("unchecked") |
| 158 @Override |
| 159 public void onAction(Object actionData) { |
| 160 RecordHistogram.recordEnumeratedHistogram("AndroidTabCloseUndo.Toast", |
| 161 TAB_CLOSE_UNDO_TOAST_PRESSED, TAB_CLOSE_UNDO_TOAST_COUNT); |
| 162 if (actionData instanceof Integer) { |
| 163 cancelTabClosure((Integer) actionData); |
| 164 } else { |
| 165 for (Integer id : (List<Integer>) actionData) { |
| 166 cancelTabClosure(id); |
| 167 } |
| 168 } |
| 169 } |
| 170 |
| 171 private void cancelTabClosure(int tabId) { |
| 172 TabModel model = mTabModelSelector.getModelForTabId(tabId); |
| 173 if (model != null) model.cancelTabClosure(tabId); |
| 174 } |
| 175 |
| 176 /** |
| 177 * Calls {@link TabModel#commitTabClosure(int)} for the tab or for each tab
in |
| 178 * the list of closed tabs. |
| 179 */ |
| 180 @SuppressWarnings("unchecked") |
| 181 @Override |
| 182 public void onDismissNoAction(Object actionData) { |
| 183 if (actionData instanceof Integer) { |
| 184 commitTabClosure((Integer) actionData); |
| 185 } else { |
| 186 for (Integer tabId : (List<Integer>) actionData) { |
| 187 commitTabClosure(tabId); |
| 188 } |
| 189 } |
| 190 } |
| 191 |
| 192 private void commitTabClosure(int tabId) { |
| 193 TabModel model = mTabModelSelector.getModelForTabId(tabId); |
| 194 if (model != null) model.commitTabClosure(tabId); |
| 195 } |
| 196 |
| 197 @Override |
| 198 public void onDismissForEachType(boolean isTimeout) { |
| 199 RecordHistogram.recordEnumeratedHistogram("AndroidTabCloseUndo.Toast", |
| 200 isTimeout ? TAB_CLOSE_UNDO_TOAST_DISMISSED_TIMEOUT |
| 201 : TAB_CLOSE_UNDO_TOAST_DISMISSED_ACTION, |
| 202 TAB_CLOSE_UNDO_TOAST_COUNT); |
| 203 } |
| 204 } |
OLD | NEW |