Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(526)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java

Issue 2878543003: Hook up Reader Mode InfoBar (Closed)
Patch Set: address comments Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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.dom_distiller; 5 package org.chromium.chrome.browser.dom_distiller;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.text.TextUtils; 8 import android.text.TextUtils;
9 9
10 import org.chromium.base.CommandLine; 10 import org.chromium.base.CommandLine;
11 import org.chromium.base.SysUtils;
12 import org.chromium.base.VisibleForTesting;
13 import org.chromium.base.library_loader.LibraryLoader; 11 import org.chromium.base.library_loader.LibraryLoader;
14 import org.chromium.base.metrics.RecordHistogram; 12 import org.chromium.base.metrics.RecordHistogram;
15 import org.chromium.chrome.browser.ChromeActivity; 13 import org.chromium.chrome.browser.ChromeActivity;
16 import org.chromium.chrome.browser.ChromeSwitches; 14 import org.chromium.chrome.browser.ChromeSwitches;
17 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState;
18 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange Reason; 15 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange Reason;
19 import org.chromium.chrome.browser.compositor.bottombar.readermode.ReaderModePan el; 16 import org.chromium.chrome.browser.infobar.ReaderModeInfoBar;
20 import org.chromium.chrome.browser.infobar.InfoBar;
21 import org.chromium.chrome.browser.infobar.InfoBarContainer;
22 import org.chromium.chrome.browser.infobar.InfoBarContainer.InfoBarContainerObse rver;
23 import org.chromium.chrome.browser.rappor.RapporServiceBridge; 17 import org.chromium.chrome.browser.rappor.RapporServiceBridge;
24 import org.chromium.chrome.browser.tab.Tab; 18 import org.chromium.chrome.browser.tab.Tab;
25 import org.chromium.chrome.browser.tabmodel.TabCreatorManager; 19 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
26 import org.chromium.chrome.browser.tabmodel.TabModel; 20 import org.chromium.chrome.browser.tabmodel.TabModel;
27 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 21 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
28 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; 22 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver;
29 import org.chromium.chrome.browser.util.AccessibilityUtil; 23 import org.chromium.chrome.browser.util.AccessibilityUtil;
30 import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver;
31 import org.chromium.components.dom_distiller.content.DistillablePageUtils; 24 import org.chromium.components.dom_distiller.content.DistillablePageUtils;
32 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; 25 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
33 import org.chromium.content_public.browser.LoadUrlParams; 26 import org.chromium.content_public.browser.LoadUrlParams;
27 import org.chromium.content_public.browser.NavigationController;
28 import org.chromium.content_public.browser.NavigationEntry;
34 import org.chromium.content_public.browser.WebContents; 29 import org.chromium.content_public.browser.WebContents;
35 import org.chromium.content_public.browser.WebContentsObserver; 30 import org.chromium.content_public.browser.WebContentsObserver;
36 import org.chromium.ui.UiUtils; 31 import org.chromium.ui.UiUtils;
37 import org.chromium.ui.base.DeviceFormFactor;
38 import org.chromium.ui.base.PageTransition; 32 import org.chromium.ui.base.PageTransition;
39 33
40 import java.util.HashMap; 34 import java.util.HashMap;
41 import java.util.Map; 35 import java.util.Map;
42 import java.util.concurrent.TimeUnit; 36 import java.util.concurrent.TimeUnit;
43 37
44 /** 38 /**
45 * Manages UI effects for reader mode including hiding and showing the 39 * Manages UI effects for reader mode including hiding and showing the
46 * reader mode and reader mode preferences toolbar icon and hiding the 40 * reader mode and reader mode preferences toolbar icon and hiding the
47 * browser controls when a reader mode page has finished loading. 41 * browser controls when a reader mode page has finished loading.
48 */ 42 */
49 public class ReaderModeManager extends TabModelSelectorTabObserver 43 public class ReaderModeManager
50 implements InfoBarContainerObserver, ReaderModeManagerDelegate { 44 extends TabModelSelectorTabObserver implements ReaderModeManagerDelegate {
51 45 /** POSSIBLE means reader mode can be entered. */
52 /**
53 * POSSIBLE means reader mode can be entered.
54 */
55 public static final int POSSIBLE = 0; 46 public static final int POSSIBLE = 0;
56 47
57 /** 48 /** NOT_POSSIBLE means reader mode cannot be entered. */
58 * NOT_POSSIBLE means reader mode cannot be entered.
59 */
60 public static final int NOT_POSSIBLE = 1; 49 public static final int NOT_POSSIBLE = 1;
61 50
62 /** 51 /** STARTED means reader mode is currently in reader mode. */
63 * STARTED means reader mode is currently in reader mode.
64 */
65 public static final int STARTED = 2; 52 public static final int STARTED = 2;
66 53
67 // The url of the last page visited if the last page was reader mode page. Otherwise null. 54 /** The url of the last page visited if the last page was reader mode page. Otherwise null. */
68 private String mReaderModePageUrl; 55 private String mReaderModePageUrl;
69 56
70 // Whether the fact that the current web page was distillable or not has bee n recorded. 57 /** Whether the fact that the current web page was distillable or not has be en recorded. */
71 private boolean mIsUmaRecorded; 58 private boolean mIsUmaRecorded;
72 59
73 // The per-tab state of distillation. 60 /** The per-tab state of distillation. */
74 protected Map<Integer, ReaderModeTabInfo> mTabStatusMap; 61 protected Map<Integer, ReaderModeTabInfo> mTabStatusMap;
75 62
76 // The current tab ID. This will change as the user switches between tabs. 63 /** The ChromeActivity that this panel exists in. */
wychen 2017/05/18 15:26:22 Do we still call it panel?
mdjones 2017/05/18 16:45:55 I renamed panel to infobar in places that will exi
77 private int mTabId;
78
79 // The ReaderModePanel that this class is managing.
80 protected ReaderModePanel mReaderModePanel;
81
82 // The ChromeActivity that this panel exists in.
83 private ChromeActivity mChromeActivity; 64 private ChromeActivity mChromeActivity;
84 65
85 // The primary means of getting the currently active tab. 66 /** The primary means of getting the currently active tab. */
86 private TabModelSelector mTabModelSelector; 67 private TabModelSelector mTabModelSelector;
87 68
88 private boolean mIsFullscreenModeEntered; 69 /** If Reader Mode is detecting all pages as distillable. */
89 private boolean mIsFindToolbarShowing;
90 private boolean mIsKeyboardShowing;
91
92 // InfoBar tracking.
93 private boolean mIsInfoBarContainerShown;
94
95 // If Reader Mode is detecting all pages as distillable.
96 private boolean mIsReaderHeuristicAlwaysTrue; 70 private boolean mIsReaderHeuristicAlwaysTrue;
97 71
98
99 public ReaderModeManager(TabModelSelector selector, ChromeActivity activity) { 72 public ReaderModeManager(TabModelSelector selector, ChromeActivity activity) {
100 super(selector); 73 super(selector);
101 mTabId = Tab.INVALID_TAB_ID;
102 mTabModelSelector = selector; 74 mTabModelSelector = selector;
103 mChromeActivity = activity; 75 mChromeActivity = activity;
104 mTabStatusMap = new HashMap<>(); 76 mTabStatusMap = new HashMap<>();
105 mIsReaderHeuristicAlwaysTrue = isDistillerHeuristicAlwaysTrue(); 77 mIsReaderHeuristicAlwaysTrue = isDistillerHeuristicAlwaysTrue();
106 } 78 }
107 79
108 /** 80 /**
109 * This function wraps a method that calls native code and is overridden by tests. 81 * This function wraps a method that calls native code and is overridden by tests.
110 * @return True if the heuristic is ALWAYS_TRUE. 82 * @return True if the heuristic is ALWAYS_TRUE.
111 */ 83 */
(...skipping 10 matching lines...) Expand all
122 for (Map.Entry<Integer, ReaderModeTabInfo> e : mTabStatusMap.entrySet()) { 94 for (Map.Entry<Integer, ReaderModeTabInfo> e : mTabStatusMap.entrySet()) {
123 if (e.getValue().getWebContentsObserver() != null) { 95 if (e.getValue().getWebContentsObserver() != null) {
124 e.getValue().getWebContentsObserver().destroy(); 96 e.getValue().getWebContentsObserver().destroy();
125 } 97 }
126 } 98 }
127 mTabStatusMap.clear(); 99 mTabStatusMap.clear();
128 100
129 DomDistillerUIUtils.destroy(this); 101 DomDistillerUIUtils.destroy(this);
130 102
131 mChromeActivity = null; 103 mChromeActivity = null;
132 mReaderModePanel = null;
133 mTabModelSelector = null; 104 mTabModelSelector = null;
134 } 105 }
135 106
136 /**
137 * @return A FindToolbarObserver capable of hiding the Reader Mode panel.
138 */
139 public FindToolbarObserver getFindToolbarObserver() {
140 return new FindToolbarObserver() {
141 @Override
142 public void onFindToolbarShown() {
143 mIsFindToolbarShowing = true;
144 closeReaderPanel(StateChangeReason.UNKNOWN, true);
145 }
146
147 @Override
148 public void onFindToolbarHidden() {
149 mIsFindToolbarShowing = false;
150 requestReaderPanelShow(StateChangeReason.UNKNOWN);
151 }
152 };
153 }
154
155 // TabModelSelectorTabObserver: 107 // TabModelSelectorTabObserver:
156 108
157 @Override 109 @Override
158 public void onShown(Tab shownTab) { 110 public void onShown(Tab shownTab) {
159 if (mTabModelSelector == null) return; 111 if (mTabModelSelector == null) return;
160 112
161 int shownTabId = shownTab.getId(); 113 int shownTabId = shownTab.getId();
162 Tab previousTab = mTabModelSelector.getTabById(mTabId);
163 mTabId = shownTabId;
164 114
165 // If the reader panel was dismissed, stop here. 115 // If the reader panel was dismissed, stop here.
wychen 2017/05/18 15:26:21 s/panel/infobar/?
mdjones 2017/05/18 16:45:55 Done.
166 if (mTabStatusMap.containsKey(shownTabId) 116 if (mTabStatusMap.containsKey(shownTabId)
167 && mTabStatusMap.get(shownTabId).isDismissed()) { 117 && mTabStatusMap.get(shownTabId).isDismissed()) {
168 return; 118 return;
169 } 119 }
170 120
171 // Set this manager as the active one for the UI utils. 121 // Set this manager as the active one for the UI utils.
172 DomDistillerUIUtils.setReaderModeManagerDelegate(this); 122 DomDistillerUIUtils.setReaderModeManagerDelegate(this);
173 123
174 // Update infobar state based on current tab.
175 if (shownTab.getInfoBarContainer() != null) {
176 mIsInfoBarContainerShown = shownTab.getInfoBarContainer().hasInfoBar s();
177 }
178
179 // Remove the infobar observer from the previous tab and attach it to th e current one.
180 if (previousTab != null && previousTab.getInfoBarContainer() != null) {
181 previousTab.getInfoBarContainer().removeObserver(this);
182 }
183
184 if (shownTab.getInfoBarContainer() != null) {
185 shownTab.getInfoBarContainer().addObserver(this);
186 }
187
188 // If there is no state info for this tab, create it. 124 // If there is no state info for this tab, create it.
189 ReaderModeTabInfo tabInfo = mTabStatusMap.get(shownTabId); 125 ReaderModeTabInfo tabInfo = mTabStatusMap.get(shownTabId);
190 if (tabInfo == null) { 126 if (tabInfo == null) {
191 tabInfo = new ReaderModeTabInfo(); 127 tabInfo = new ReaderModeTabInfo();
192 tabInfo.setStatus(NOT_POSSIBLE); 128 tabInfo.setStatus(NOT_POSSIBLE);
193 tabInfo.setUrl(shownTab.getUrl()); 129 tabInfo.setUrl(shownTab.getUrl());
194 mTabStatusMap.put(shownTabId, tabInfo); 130 mTabStatusMap.put(shownTabId, tabInfo);
195 } 131 }
196 132
133 if (DomDistillerUrlUtils.isDistilledPage(shownTab.getUrl())
134 && !tabInfo.isViewingReaderModePage()) {
135 tabInfo.onStartedReaderMode();
136 }
137
197 // Make sure there is a WebContentsObserver on this tab's WebContents. 138 // Make sure there is a WebContentsObserver on this tab's WebContents.
198 if (tabInfo.getWebContentsObserver() == null) { 139 if (tabInfo.getWebContentsObserver() == null) {
199 tabInfo.setWebContentsObserver(createWebContentsObserver(shownTab.ge tWebContents())); 140 tabInfo.setWebContentsObserver(createWebContentsObserver(shownTab.ge tWebContents()));
200 } 141 }
201 142
202 // Make sure there is a distillability delegate set on the WebContents. 143 // Make sure there is a distillability delegate set on the WebContents.
203 setDistillabilityCallback(shownTabId); 144 setDistillabilityCallback(shownTabId);
204 145
205 requestReaderPanelShow(StateChangeReason.UNKNOWN); 146 tryShowingInfoBar();
206 } 147 }
207 148
208 @Override 149 @Override
209 public void onHidden(Tab tab) { 150 public void onHidden(Tab tab) {
210 closeReaderPanel(StateChangeReason.UNKNOWN, false); 151 closeReaderPanel(StateChangeReason.UNKNOWN, false);
152 ReaderModeTabInfo info = mTabStatusMap.get(tab.getId());
153 if (info != null && info.isViewingReaderModePage()) {
154 long timeMs = info.onExitReaderMode();
155 recordReaderModeViewDuration(timeMs);
156 }
211 } 157 }
212 158
213 @Override 159 @Override
214 public void onDestroyed(Tab tab) { 160 public void onDestroyed(Tab tab) {
215 if (tab == null) return; 161 if (tab == null) return;
216 if (tab.getInfoBarContainer() != null) { 162
217 tab.getInfoBarContainer().removeObserver(this);
218 }
219 // If the panel was not shown for the previous navigation, record it now . 163 // If the panel was not shown for the previous navigation, record it now .
220 ReaderModeTabInfo info = mTabStatusMap.get(tab.getId()); 164 ReaderModeTabInfo info = mTabStatusMap.get(tab.getId());
221 if (info != null && !info.isPanelShowRecorded()) { 165 if (info != null) {
222 recordPanelVisibilityForNavigation(false); 166 if (!info.isPanelShowRecorded()) {
167 recordPanelVisibilityForNavigation(false);
168 }
169 if (info.isViewingReaderModePage()) {
170 long timeMs = info.onExitReaderMode();
171 recordReaderModeViewDuration(timeMs);
172 }
223 } 173 }
224 removeTabState(tab.getId()); 174 removeTabState(tab.getId());
225 } 175 }
226 176
227 /** 177 /**
228 * Clean up the state associated with a tab. 178 * Clean up the state associated with a tab.
229 * @param tabId The target tab ID. 179 * @param tabId The target tab ID.
230 */ 180 */
231 private void removeTabState(int tabId) { 181 private void removeTabState(int tabId) {
232 if (!mTabStatusMap.containsKey(tabId)) return; 182 if (!mTabStatusMap.containsKey(tabId)) return;
233 ReaderModeTabInfo tabInfo = mTabStatusMap.get(tabId); 183 ReaderModeTabInfo tabInfo = mTabStatusMap.get(tabId);
234 if (tabInfo.getWebContentsObserver() != null) { 184 if (tabInfo.getWebContentsObserver() != null) {
235 tabInfo.getWebContentsObserver().destroy(); 185 tabInfo.getWebContentsObserver().destroy();
236 } 186 }
237 mTabStatusMap.remove(tabId); 187 mTabStatusMap.remove(tabId);
238 } 188 }
239 189
240 @Override 190 @Override
241 public void onContentChanged(Tab tab) { 191 public void onContentChanged(Tab tab) {
242 // Only listen to events on the currently active tab. 192 // If the content change was because of distiller switching web contents or Reader Mode has
243 if (tab.getId() != mTabId) return; 193 // already been dismissed for this tab do nothing.
244 closeReaderPanel(StateChangeReason.UNKNOWN, false); 194 if (mTabStatusMap.containsKey(tab.getId()) && mTabStatusMap.get(tab.getI d()).isDismissed()
245 195 && !DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) {
246 if (mTabStatusMap.containsKey(mTabId)) { 196 return;
247 // If the panel was closed using the "x" icon, don't show it again f or this tab.
248 if (mTabStatusMap.get(mTabId).isDismissed()) return;
249 removeTabState(mTabId);
250 } 197 }
251 198
252 ReaderModeTabInfo tabInfo = new ReaderModeTabInfo(); 199 ReaderModeTabInfo tabInfo = mTabStatusMap.get(tab.getId());
200 if (!mTabStatusMap.containsKey(tab.getId())) {
201 tabInfo = new ReaderModeTabInfo();
202 mTabStatusMap.put(tab.getId(), tabInfo);
203 }
204 // If the tab state already existed, only reset the relevant data. Thing s like view duration
205 // need to be preserved.
253 tabInfo.setStatus(NOT_POSSIBLE); 206 tabInfo.setStatus(NOT_POSSIBLE);
254 tabInfo.setUrl(tab.getUrl()); 207 tabInfo.setUrl(tab.getUrl());
255 mTabStatusMap.put(tab.getId(), tabInfo); 208 tabInfo.setIsCallbackSet(false);
256 209
257 if (tab.getWebContents() != null) { 210 if (tab.getWebContents() != null) {
258 tabInfo.setWebContentsObserver(createWebContentsObserver(tab.getWebC ontents())); 211 tabInfo.setWebContentsObserver(createWebContentsObserver(tab.getWebC ontents()));
259 if (DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) { 212 if (DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) {
260 tabInfo.setStatus(STARTED); 213 tabInfo.setStatus(STARTED);
261 mReaderModePageUrl = tab.getUrl(); 214 mReaderModePageUrl = tab.getUrl();
262 closeReaderPanel(StateChangeReason.CONTENT_CHANGED, true); 215 closeReaderPanel(StateChangeReason.CONTENT_CHANGED, true);
263 } 216 }
264 // Make sure there is a distillability delegate set on the WebConten ts. 217 // Make sure there is a distillability delegate set on the WebConten ts.
265 setDistillabilityCallback(tab.getId()); 218 setDistillabilityCallback(tab.getId());
266 } 219 }
267
268 if (tab.getInfoBarContainer() != null) tab.getInfoBarContainer().addObse rver(this);
269 }
270
271 @Override
272 public void onToggleFullscreenMode(Tab tab, boolean enable) {
273 // Temporarily hide the reader mode panel while fullscreen is enabled.
274 if (enable) {
275 mIsFullscreenModeEntered = true;
276 closeReaderPanel(StateChangeReason.FULLSCREEN_ENTERED, false);
277 } else {
278 mIsFullscreenModeEntered = false;
279 requestReaderPanelShow(StateChangeReason.FULLSCREEN_EXITED);
280 }
281 }
282
283 // InfoBarContainerObserver:
284
285 @Override
286 public void onAddInfoBar(InfoBarContainer container, InfoBar infoBar, boolea n isFirst) {
287 mIsInfoBarContainerShown = true;
288 // If the panel is opened past the peeking state, obscure the infobar.
289 if (mReaderModePanel != null && mReaderModePanel.isPanelOpened() && cont ainer != null) {
290 container.setIsObscuredByOtherView(true);
291 } else if (isFirst) {
292 // Temporarily hides the reader mode button while the infobars are s hown.
293 closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
294 }
295 }
296
297 @Override
298 public void onRemoveInfoBar(InfoBarContainer container, InfoBar infoBar, boo lean isLast) {
299 // Re-shows the reader mode button if necessary once the infobars are di smissed.
300 if (isLast) {
301 mIsInfoBarContainerShown = false;
302 requestReaderPanelShow(StateChangeReason.INFOBAR_HIDDEN);
303 }
304 }
305
306 @Override
307 public void onInfoBarContainerAttachedToWindow(boolean hasInfoBars) {
308 mIsInfoBarContainerShown = hasInfoBars;
309 if (mIsInfoBarContainerShown) {
310 closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
311 } else {
312 requestReaderPanelShow(StateChangeReason.INFOBAR_HIDDEN);
313 }
314 } 220 }
315 221
316 // ReaderModeManagerDelegate: 222 // ReaderModeManagerDelegate:
317 223
318 @Override 224 @Override
319 public void setReaderModePanel(ReaderModePanel panel) {
320 mReaderModePanel = panel;
321 }
322
323 @Override
324 public ChromeActivity getChromeActivity() { 225 public ChromeActivity getChromeActivity() {
325 return mChromeActivity; 226 return mChromeActivity;
326 } 227 }
327 228
328 @Override 229 @Override
329 public void onPanelShown() { 230 public void onPanelShown() {
330 if (mTabModelSelector == null) return; 231 if (mTabModelSelector == null) return;
331 int tabId = mTabModelSelector.getCurrentTabId(); 232 int tabId = mTabModelSelector.getCurrentTabId();
332 233
333 ReaderModeTabInfo info = mTabStatusMap.get(tabId); 234 ReaderModeTabInfo info = mTabStatusMap.get(tabId);
(...skipping 10 matching lines...) Expand all
344 /** 245 /**
345 * Record if the panel became visible on the current page. This can be overr idden for testing. 246 * Record if the panel became visible on the current page. This can be overr idden for testing.
346 * @param visible If the panel was visible at any time. 247 * @param visible If the panel was visible at any time.
347 */ 248 */
348 protected void recordPanelVisibilityForNavigation(boolean visible) { 249 protected void recordPanelVisibilityForNavigation(boolean visible) {
349 RecordHistogram.recordBooleanHistogram("DomDistiller.ReaderShownForPageL oad", visible); 250 RecordHistogram.recordBooleanHistogram("DomDistiller.ReaderShownForPageL oad", visible);
350 } 251 }
351 252
352 @Override 253 @Override
353 public void onClosed(StateChangeReason reason) { 254 public void onClosed(StateChangeReason reason) {
354 if (mReaderModePanel == null || mTabModelSelector == null) return; 255 if (mTabModelSelector == null) return;
355 256
356 restoreInfobars(); 257 RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", fals e);
357
358 // Only dismiss the panel if the close was a result of user interaction.
359 if (reason != StateChangeReason.FLING && reason != StateChangeReason.SWI PE
360 && reason != StateChangeReason.CLOSE_BUTTON) {
361 return;
362 }
363
364 // Record close button usage.
365 if (reason == StateChangeReason.CLOSE_BUTTON) {
366 RecordHistogram.recordBooleanHistogram("DomDistiller.BarCloseButtonU sage",
367 mReaderModePanel.getPanelState() == PanelState.EXPANDED
368 || mReaderModePanel.getPanelState() == PanelState.MAXIMIZED) ;
369 }
370 258
371 int currentTabId = mTabModelSelector.getCurrentTabId(); 259 int currentTabId = mTabModelSelector.getCurrentTabId();
372 if (!mTabStatusMap.containsKey(currentTabId)) return; 260 if (!mTabStatusMap.containsKey(currentTabId)) return;
373 mTabStatusMap.get(currentTabId).setIsDismissed(true); 261 mTabStatusMap.get(currentTabId).setIsDismissed(true);
374 } 262 }
375 263
376 @Override 264 @Override
377 public void onPeek() {
378 restoreInfobars();
379 }
380
381 /**
382 * Restore any infobars that may have been hidden by Reader Mode.
383 */
384 private void restoreInfobars() {
385 if (!mIsInfoBarContainerShown) return;
386
387 Tab curTab = mTabModelSelector.getCurrentTab();
388 if (curTab == null) return;
389
390 InfoBarContainer container = curTab.getInfoBarContainer();
391 if (container == null) return;
392
393 container.setIsObscuredByOtherView(false);
394
395 // Temporarily hides the reader mode button while the infobars are shown .
396 closeReaderPanel(StateChangeReason.INFOBAR_SHOWN, false);
397 }
398
399 @Override
400 public WebContents getBasePageWebContents() { 265 public WebContents getBasePageWebContents() {
401 Tab tab = mTabModelSelector.getCurrentTab(); 266 Tab tab = mTabModelSelector.getCurrentTab();
402 if (tab == null) return null; 267 if (tab == null) return null;
403 268
404 return tab.getWebContents(); 269 return tab.getWebContents();
405 } 270 }
406 271
407 @Override 272 @Override
408 public void closeReaderPanel(StateChangeReason reason, boolean animate) { 273 public void closeReaderPanel(StateChangeReason reason, boolean animate) {
409 if (mReaderModePanel == null) return; 274 // TODO(mdjones): Remove this method and dependencies.
410 mReaderModePanel.closePanel(reason, animate);
411 } 275 }
412 276
413 @Override 277 @Override
414 public void recordTimeSpentInReader(long timeMs) { 278 public void recordTimeSpentInReader(long timeMs) {
415 RecordHistogram.recordLongTimesHistogram("DomDistiller.Time.ViewingReade rModePanel", 279 RecordHistogram.recordLongTimesHistogram("DomDistiller.Time.ViewingReade rModePanel",
416 timeMs, TimeUnit.MILLISECONDS); 280 timeMs, TimeUnit.MILLISECONDS);
417 } 281 }
418 282
419 @Override
420 public void onLayoutChanged() {
421 if (isKeyboardShowing()) {
422 mIsKeyboardShowing = true;
423 closeReaderPanel(StateChangeReason.KEYBOARD_SHOWN, false);
424 } else if (mIsKeyboardShowing) {
425 mIsKeyboardShowing = false;
426 requestReaderPanelShow(StateChangeReason.KEYBOARD_HIDDEN);
427 }
428 }
429
430 /** 283 /**
431 * @return True if the keyboard might be showing. This is not 100% accurate; see 284 * @return True if the keyboard might be showing. This is not 100% accurate; see
432 * UiUtils.isKeyboardShowing(...). 285 * UiUtils.isKeyboardShowing(...).
433 */ 286 */
434 protected boolean isKeyboardShowing() { 287 protected boolean isKeyboardShowing() {
435 return mChromeActivity != null && UiUtils.isKeyboardShowing(mChromeActiv ity, 288 return mChromeActivity != null && UiUtils.isKeyboardShowing(mChromeActiv ity,
436 mChromeActivity.findViewById(android.R.id.content)); 289 mChromeActivity.findViewById(android.R.id.content));
437 } 290 }
438 291
439 protected WebContentsObserver createWebContentsObserver(WebContents webConte nts) { 292 protected WebContentsObserver createWebContentsObserver(final WebContents we bContents) {
440 final int readerTabId = mTabModelSelector.getCurrentTabId(); 293 final int readerTabId = mTabModelSelector.getCurrentTabId();
441 if (readerTabId == Tab.INVALID_TAB_ID) return null; 294 if (readerTabId == Tab.INVALID_TAB_ID) return null;
442 295
443 return new WebContentsObserver(webContents) { 296 return new WebContentsObserver(webContents) {
297 /** Whether or not the previous navigation should be removed. */
298 private boolean mShouldRemovePreviousNavigation;
299
300 /** The index of the last committed distiller page in history. */
301 private int mLastDistillerPageIndex;
302
444 @Override 303 @Override
445 public void didStartNavigation(String url, boolean isInMainFrame, 304 public void didStartNavigation(String url, boolean isInMainFrame,
446 boolean isSameDocument, boolean isErrorPage) { 305 boolean isSameDocument, boolean isErrorPage) {
447 if (!isInMainFrame || isSameDocument) return; 306 if (!isInMainFrame || isSameDocument) return;
307
308 // Reader Mode should not pollute the navigation stack. To avoid this, watch for
309 // navigations and prepare to remove any that are "chrome-distil ler" urls.
310 NavigationController controller = webContents.getNavigationContr oller();
311 int index = controller.getLastCommittedEntryIndex();
312 NavigationEntry entry = controller.getEntryAtIndex(index);
313
314 if (entry != null && DomDistillerUrlUtils.isDistilledPage(entry. getUrl())) {
315 mShouldRemovePreviousNavigation = true;
316 mLastDistillerPageIndex = index;
317 }
318
448 // If there is a navigation in the current tab, hide the bar. It will show again 319 // If there is a navigation in the current tab, hide the bar. It will show again
449 // once the distillability test is successful. 320 // once the distillability test is successful.
450 if (readerTabId == mTabModelSelector.getCurrentTabId()) { 321 if (readerTabId == mTabModelSelector.getCurrentTabId()) {
451 closeReaderPanel(StateChangeReason.TAB_NAVIGATION, false); 322 closeReaderPanel(StateChangeReason.TAB_NAVIGATION, false);
452 } 323 }
453 324
454 // Make sure the tab was not destroyed. 325 // Make sure the tab was not destroyed.
455 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); 326 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId);
456 if (tabInfo == null) return; 327 if (tabInfo == null) return;
457 328
458 tabInfo.setUrl(url); 329 tabInfo.setUrl(url);
459 if (DomDistillerUrlUtils.isDistilledPage(url)) { 330 if (DomDistillerUrlUtils.isDistilledPage(url)) {
460 tabInfo.setStatus(STARTED); 331 tabInfo.setStatus(STARTED);
461 mReaderModePageUrl = url; 332 mReaderModePageUrl = url;
462 } 333 }
463 } 334 }
464 335
465 @Override 336 @Override
466 public void didFinishNavigation(String url, boolean isInMainFrame, b oolean isErrorPage, 337 public void didFinishNavigation(String url, boolean isInMainFrame, b oolean isErrorPage,
467 boolean hasCommitted, boolean isSameDocument, boolean isFrag mentNavigation, 338 boolean hasCommitted, boolean isSameDocument, boolean isFrag mentNavigation,
468 Integer pageTransition, int errorCode, String errorDescripti on, 339 Integer pageTransition, int errorCode, String errorDescripti on,
469 int httpStatusCode) { 340 int httpStatusCode) {
470 // TODO(cjhopman): This should possibly ignore navigations that replace the entry 341 // TODO(cjhopman): This should possibly ignore navigations that replace the entry
471 // (like those from history.replaceState()). 342 // (like those from history.replaceState()).
472 if (!hasCommitted || !isInMainFrame || isSameDocument) return; 343 if (!hasCommitted || !isInMainFrame || isSameDocument) return;
473 if (DomDistillerUrlUtils.isDistilledPage(url)) return; 344
345 if (mShouldRemovePreviousNavigation) {
346 mShouldRemovePreviousNavigation = false;
347 NavigationController controller = webContents.getNavigationC ontroller();
348 controller.removeEntryAtIndex(mLastDistillerPageIndex);
349 }
474 350
475 // Make sure the tab was not destroyed. 351 // Make sure the tab was not destroyed.
476 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); 352 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId);
477 if (tabInfo == null) return; 353 if (tabInfo == null) return;
478 354
479 tabInfo.setStatus(POSSIBLE); 355 tabInfo.setStatus(POSSIBLE);
480 if (!TextUtils.equals(url, 356 if (!TextUtils.equals(url,
481 DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl( 357 DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(
482 mReaderModePageUrl))) { 358 mReaderModePageUrl))) {
483 tabInfo.setStatus(NOT_POSSIBLE); 359 tabInfo.setStatus(NOT_POSSIBLE);
484 mIsUmaRecorded = false; 360 mIsUmaRecorded = false;
485 } 361 }
486 mReaderModePageUrl = null; 362 mReaderModePageUrl = null;
487 363
488 if (tabInfo.getStatus() != POSSIBLE) { 364 if (tabInfo.getStatus() != POSSIBLE) {
489 closeReaderPanel(StateChangeReason.UNKNOWN, false); 365 closeReaderPanel(StateChangeReason.UNKNOWN, false);
490 } else { 366 } else {
491 requestReaderPanelShow(StateChangeReason.UNKNOWN); 367 tryShowingInfoBar();
492 } 368 }
493 } 369 }
494 370
495 @Override 371 @Override
496 public void navigationEntryCommitted() { 372 public void navigationEntryCommitted() {
497 // Make sure the tab was not destroyed. 373 // Make sure the tab was not destroyed.
498 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); 374 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId);
499 if (tabInfo == null) return; 375 if (tabInfo == null) return;
500 // Reset closed state of reader mode in this tab once we know a navigation is 376 // Reset closed state of reader mode in this tab once we know a navigation is
501 // happening. 377 // happening.
502 tabInfo.setIsDismissed(false); 378 tabInfo.setIsDismissed(false);
503 379
504 // If the panel was not shown for the previous navigation, recor d it now. 380 // If the panel was not shown for the previous navigation, recor d it now.
505 Tab curTab = mTabModelSelector.getTabById(readerTabId); 381 Tab curTab = mTabModelSelector.getTabById(readerTabId);
506 if (curTab != null && !curTab.isNativePage() && !curTab.isBeingR estored()) { 382 if (curTab != null && !curTab.isNativePage() && !curTab.isBeingR estored()) {
507 recordPanelVisibilityForNavigation(false); 383 recordPanelVisibilityForNavigation(false);
508 } 384 }
509 tabInfo.setIsPanelShowRecorded(false); 385 tabInfo.setIsPanelShowRecorded(false);
386
387 if (!DomDistillerUrlUtils.isDistilledPage(curTab.getUrl())
388 && tabInfo.isViewingReaderModePage()) {
389 long timeMs = tabInfo.onExitReaderMode();
390 recordReaderModeViewDuration(timeMs);
391 }
510 } 392 }
511 }; 393 };
512 } 394 }
513 395
514 /** 396 /**
515 * This is a wrapper for "requestPanelShow" that checks if reader mode is po ssible before 397 * Record the amount of time the user spent in Reader Mode.
516 * showing. 398 * @param timeMs The amount of time in ms that the user spent in Reader Mode .
517 * @param reason The reason the panel is requesting to be shown.
518 */ 399 */
519 protected void requestReaderPanelShow(StateChangeReason reason) { 400 private void recordReaderModeViewDuration(long timeMs) {
401 RecordHistogram.recordLongTimesHistogram(
402 "DomDistiller.Time.ViewingReaderModePage", timeMs, TimeUnit.MILL ISECONDS);
403 }
404
405 /**
406 * Try showing the reader mode infobar.
407 */
408 protected void tryShowingInfoBar() {
520 if (mTabModelSelector == null) return; 409 if (mTabModelSelector == null) return;
521 410
522 int currentTabId = mTabModelSelector.getCurrentTabId(); 411 int currentTabId = mTabModelSelector.getCurrentTabId();
523 if (currentTabId == Tab.INVALID_TAB_ID) return; 412 if (currentTabId == Tab.INVALID_TAB_ID) return;
524 413
525 // Test if the user is requesting the desktop site. Ignore this if disti ller is set to 414 // Test if the user is requesting the desktop site. Ignore this if disti ller is set to
526 // ALWAYS_TRUE. 415 // ALWAYS_TRUE.
527 boolean usingRequestDesktopSite = getBasePageWebContents() != null 416 boolean usingRequestDesktopSite = getBasePageWebContents() != null
528 && getBasePageWebContents().getNavigationController().getUseDesk topUserAgent() 417 && getBasePageWebContents().getNavigationController().getUseDesk topUserAgent()
529 && !mIsReaderHeuristicAlwaysTrue; 418 && !mIsReaderHeuristicAlwaysTrue;
530 419
531 if (mReaderModePanel == null || !mTabStatusMap.containsKey(currentTabId) 420 if (!mTabStatusMap.containsKey(currentTabId) || usingRequestDesktopSite
532 || usingRequestDesktopSite
533 || mTabStatusMap.get(currentTabId).getStatus() != POSSIBLE 421 || mTabStatusMap.get(currentTabId).getStatus() != POSSIBLE
534 || mTabStatusMap.get(currentTabId).isDismissed() 422 || mTabStatusMap.get(currentTabId).isDismissed()
535 || mIsInfoBarContainerShown
536 || mIsFindToolbarShowing
537 || mIsFullscreenModeEntered
538 || mIsKeyboardShowing
539 || AccessibilityUtil.isAccessibilityEnabled()) { 423 || AccessibilityUtil.isAccessibilityEnabled()) {
540 return; 424 return;
541 } 425 }
542 426
543 mReaderModePanel.requestPanelShow(reason); 427 ReaderModeInfoBar.showReaderModeInfoBar(mTabModelSelector.getCurrentTab( ), this);
544 } 428 }
545 429
546 /** 430 /**
431 * Navigate the current tab to a Reader Mode URL.
432 */
433 public void navigateToReaderMode() {
434 RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", true );
435
436 WebContents baseWebContents = getBasePageWebContents();
437 if (baseWebContents == null || mChromeActivity == null || mTabModelSelec tor == null) return;
438
439 String url = baseWebContents.getUrl();
440 if (url == null) return;
441
442 ReaderModeTabInfo info = mTabStatusMap.get(mTabModelSelector.getCurrentT abId());
443 if (info != null) info.onStartedReaderMode();
444
445 DomDistillerTabUtils.distillCurrentPageAndView(getBasePageWebContents()) ;
446 }
447
448 /**
547 * Open a link from the panel in a new tab. 449 * Open a link from the panel in a new tab.
548 * @param url The URL to load. 450 * @param url The URL to load.
549 */ 451 */
550 @Override 452 @Override
551 public void createNewTab(String url) { 453 public void createNewTab(String url) {
552 if (mChromeActivity == null) return; 454 if (mChromeActivity == null) return;
553 455
554 Tab currentTab = mTabModelSelector.getCurrentTab(); 456 Tab currentTab = mTabModelSelector.getCurrentTab();
555 if (currentTab == null) return; 457 if (currentTab == null) return;
556 458
557 TabCreatorManager.TabCreator tabCreator = 459 TabCreatorManager.TabCreator tabCreator =
558 mChromeActivity.getTabCreator(currentTab.isIncognito()); 460 mChromeActivity.getTabCreator(currentTab.isIncognito());
559 if (tabCreator == null) return; 461 if (tabCreator == null) return;
560 462
561 tabCreator.createNewTab(new LoadUrlParams(url, PageTransition.LINK), 463 tabCreator.createNewTab(new LoadUrlParams(url, PageTransition.LINK),
562 TabModel.TabLaunchType.FROM_LINK, mChromeActivity.getActivityTab ()); 464 TabModel.TabLaunchType.FROM_LINK, mChromeActivity.getActivityTab ());
563 } 465 }
564 466
565 /** 467 /**
566 * @return Whether the Reader Mode panel is opened (state is EXPANDED or MAX IMIZED).
567 */
568 public boolean isPanelOpened() {
569 if (mReaderModePanel == null) return false;
570 return mReaderModePanel.isPanelOpened();
571 }
572
573 /**
574 * @return The ReaderModePanel for testing.
575 */
576 @VisibleForTesting
577 public ReaderModePanel getPanelForTesting() {
578 return mReaderModePanel;
579 }
580
581 /**
582 * Set the callback for updating reader mode status based on whether or not the page should 468 * Set the callback for updating reader mode status based on whether or not the page should
583 * be viewed in reader mode. 469 * be viewed in reader mode.
584 * @param tabId The ID of the tab having its callback set. 470 * @param tabId The ID of the tab having its callback set.
585 */ 471 */
586 private void setDistillabilityCallback(final int tabId) { 472 private void setDistillabilityCallback(final int tabId) {
587 if (tabId == Tab.INVALID_TAB_ID || mTabStatusMap.get(tabId).isCallbackSe t()) { 473 if (tabId == Tab.INVALID_TAB_ID || mTabStatusMap.get(tabId).isCallbackSe t()) {
588 return; 474 return;
589 } 475 }
590 476
591 if (mTabModelSelector == null) return; 477 if (mTabModelSelector == null) return;
(...skipping 18 matching lines...) Expand all
610 // destroyed so that this never happens. 496 // destroyed so that this never happens.
611 if (readerTab == null || tabInfo == null) return; 497 if (readerTab == null || tabInfo == null) return;
612 498
613 // Make sure the page didn't navigate while waiting for a response. 499 // Make sure the page didn't navigate while waiting for a response.
614 if (!readerTab.getUrl().equals(tabInfo.getUrl())) return ; 500 if (!readerTab.getUrl().equals(tabInfo.getUrl())) return ;
615 501
616 if (isDistillable) { 502 if (isDistillable) {
617 tabInfo.setStatus(POSSIBLE); 503 tabInfo.setStatus(POSSIBLE);
618 // The user may have changed tabs. 504 // The user may have changed tabs.
619 if (tabId == mTabModelSelector.getCurrentTabId()) { 505 if (tabId == mTabModelSelector.getCurrentTabId()) {
620 // TODO(mdjones): Add reason DISTILLER_STATE_CHA NGE. 506 tryShowingInfoBar();
621 requestReaderPanelShow(StateChangeReason.UNKNOWN );
622 } 507 }
623 } else { 508 } else {
624 tabInfo.setStatus(NOT_POSSIBLE); 509 tabInfo.setStatus(NOT_POSSIBLE);
625 } 510 }
626 if (!mIsUmaRecorded && (tabInfo.getStatus() == POSSIBLE || isLast)) { 511 if (!mIsUmaRecorded && (tabInfo.getStatus() == POSSIBLE || isLast)) {
627 mIsUmaRecorded = true; 512 mIsUmaRecorded = true;
628 RecordHistogram.recordBooleanHistogram( 513 RecordHistogram.recordBooleanHistogram(
629 "DomDistiller.PageDistillable", 514 "DomDistiller.PageDistillable",
630 tabInfo.getStatus() == POSSIBLE); 515 tabInfo.getStatus() == POSSIBLE);
631 } 516 }
632 } 517 }
633 }); 518 });
634 mTabStatusMap.get(tabId).setIsCallbackSet(true); 519 mTabStatusMap.get(tabId).setIsCallbackSet(true);
635 } 520 }
636 521
637 /** 522 /**
638 * @return Whether Reader mode and its new UI are enabled. 523 * @return Whether Reader mode and its new UI are enabled.
639 * @param context A context 524 * @param context A context
640 */ 525 */
641 public static boolean isEnabled(Context context) { 526 public static boolean isEnabled(Context context) {
642 if (context == null) return false; 527 if (context == null) return false;
643 528
644 boolean enabled = CommandLine.getInstance().hasSwitch(ChromeSwitches.ENA BLE_DOM_DISTILLER) 529 boolean enabled = CommandLine.getInstance().hasSwitch(ChromeSwitches.ENA BLE_DOM_DISTILLER)
645 && !CommandLine.getInstance().hasSwitch( 530 && !CommandLine.getInstance().hasSwitch(
646 ChromeSwitches.DISABLE_READER_MODE_BOTTOM_BAR) 531 ChromeSwitches.DISABLE_READER_MODE_BOTTOM_BAR)
647 && !DeviceFormFactor.isTablet(context) 532 && DomDistillerTabUtils.isDistillerHeuristicsEnabled();
648 && DomDistillerTabUtils.isDistillerHeuristicsEnabled()
649 && !SysUtils.isLowEndDevice();
650 return enabled; 533 return enabled;
651 } 534 }
652 } 535 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698