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.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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |