Chromium Code Reviews| 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 current tab ID. This will change as the user switches between tabs. */ |
| 77 private int mTabId; | 64 private int mTabId; |
| 78 | 65 |
| 79 // The ReaderModePanel that this class is managing. | 66 /** The ChromeActivity that this panel exists in. */ |
| 80 protected ReaderModePanel mReaderModePanel; | |
| 81 | |
| 82 // The ChromeActivity that this panel exists in. | |
| 83 private ChromeActivity mChromeActivity; | 67 private ChromeActivity mChromeActivity; |
| 84 | 68 |
| 85 // The primary means of getting the currently active tab. | 69 /** The primary means of getting the currently active tab. */ |
| 86 private TabModelSelector mTabModelSelector; | 70 private TabModelSelector mTabModelSelector; |
| 87 | 71 |
| 88 private boolean mIsFullscreenModeEntered; | 72 /** 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; | 73 private boolean mIsReaderHeuristicAlwaysTrue; |
| 97 | 74 |
| 75 /** Whether or not the previous navigation should be removed. */ | |
| 76 private boolean mShouldRemovePreviousNavigation; | |
| 77 | |
| 78 /** The index of the last committed distiller page in history. */ | |
| 79 private int mLastDistillerPageIndex; | |
| 98 | 80 |
| 99 public ReaderModeManager(TabModelSelector selector, ChromeActivity activity) { | 81 public ReaderModeManager(TabModelSelector selector, ChromeActivity activity) { |
| 100 super(selector); | 82 super(selector); |
| 101 mTabId = Tab.INVALID_TAB_ID; | 83 mTabId = Tab.INVALID_TAB_ID; |
| 102 mTabModelSelector = selector; | 84 mTabModelSelector = selector; |
| 103 mChromeActivity = activity; | 85 mChromeActivity = activity; |
| 104 mTabStatusMap = new HashMap<>(); | 86 mTabStatusMap = new HashMap<>(); |
| 105 mIsReaderHeuristicAlwaysTrue = isDistillerHeuristicAlwaysTrue(); | 87 mIsReaderHeuristicAlwaysTrue = isDistillerHeuristicAlwaysTrue(); |
| 106 } | 88 } |
| 107 | 89 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 122 for (Map.Entry<Integer, ReaderModeTabInfo> e : mTabStatusMap.entrySet()) { | 104 for (Map.Entry<Integer, ReaderModeTabInfo> e : mTabStatusMap.entrySet()) { |
| 123 if (e.getValue().getWebContentsObserver() != null) { | 105 if (e.getValue().getWebContentsObserver() != null) { |
| 124 e.getValue().getWebContentsObserver().destroy(); | 106 e.getValue().getWebContentsObserver().destroy(); |
| 125 } | 107 } |
| 126 } | 108 } |
| 127 mTabStatusMap.clear(); | 109 mTabStatusMap.clear(); |
| 128 | 110 |
| 129 DomDistillerUIUtils.destroy(this); | 111 DomDistillerUIUtils.destroy(this); |
| 130 | 112 |
| 131 mChromeActivity = null; | 113 mChromeActivity = null; |
| 132 mReaderModePanel = null; | |
| 133 mTabModelSelector = null; | 114 mTabModelSelector = null; |
| 134 } | 115 } |
| 135 | 116 |
| 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: | 117 // TabModelSelectorTabObserver: |
| 156 | 118 |
| 157 @Override | 119 @Override |
| 158 public void onShown(Tab shownTab) { | 120 public void onShown(Tab shownTab) { |
| 159 if (mTabModelSelector == null) return; | 121 if (mTabModelSelector == null) return; |
| 160 | 122 |
| 161 int shownTabId = shownTab.getId(); | 123 int shownTabId = shownTab.getId(); |
| 162 Tab previousTab = mTabModelSelector.getTabById(mTabId); | |
| 163 mTabId = shownTabId; | 124 mTabId = shownTabId; |
| 164 | 125 |
| 165 // If the reader panel was dismissed, stop here. | 126 // If the reader panel was dismissed, stop here. |
| 166 if (mTabStatusMap.containsKey(shownTabId) | 127 if (mTabStatusMap.containsKey(shownTabId) |
| 167 && mTabStatusMap.get(shownTabId).isDismissed()) { | 128 && mTabStatusMap.get(shownTabId).isDismissed()) { |
| 168 return; | 129 return; |
| 169 } | 130 } |
| 170 | 131 |
| 171 // Set this manager as the active one for the UI utils. | 132 // Set this manager as the active one for the UI utils. |
| 172 DomDistillerUIUtils.setReaderModeManagerDelegate(this); | 133 DomDistillerUIUtils.setReaderModeManagerDelegate(this); |
| 173 | 134 |
| 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. | 135 // If there is no state info for this tab, create it. |
| 189 ReaderModeTabInfo tabInfo = mTabStatusMap.get(shownTabId); | 136 ReaderModeTabInfo tabInfo = mTabStatusMap.get(shownTabId); |
| 190 if (tabInfo == null) { | 137 if (tabInfo == null) { |
| 191 tabInfo = new ReaderModeTabInfo(); | 138 tabInfo = new ReaderModeTabInfo(); |
| 192 tabInfo.setStatus(NOT_POSSIBLE); | 139 tabInfo.setStatus(NOT_POSSIBLE); |
| 193 tabInfo.setUrl(shownTab.getUrl()); | 140 tabInfo.setUrl(shownTab.getUrl()); |
| 194 mTabStatusMap.put(shownTabId, tabInfo); | 141 mTabStatusMap.put(shownTabId, tabInfo); |
| 195 } | 142 } |
| 196 | 143 |
| 197 // Make sure there is a WebContentsObserver on this tab's WebContents. | 144 // Make sure there is a WebContentsObserver on this tab's WebContents. |
| 198 if (tabInfo.getWebContentsObserver() == null) { | 145 if (tabInfo.getWebContentsObserver() == null) { |
| 199 tabInfo.setWebContentsObserver(createWebContentsObserver(shownTab.ge tWebContents())); | 146 tabInfo.setWebContentsObserver(createWebContentsObserver(shownTab.ge tWebContents())); |
| 200 } | 147 } |
| 201 | 148 |
| 202 // Make sure there is a distillability delegate set on the WebContents. | 149 // Make sure there is a distillability delegate set on the WebContents. |
| 203 setDistillabilityCallback(shownTabId); | 150 setDistillabilityCallback(shownTabId); |
| 204 | 151 |
| 205 requestReaderPanelShow(StateChangeReason.UNKNOWN); | 152 tryShowingInfoBar(); |
| 206 } | 153 } |
| 207 | 154 |
| 208 @Override | 155 @Override |
| 209 public void onHidden(Tab tab) { | 156 public void onHidden(Tab tab) { |
| 210 closeReaderPanel(StateChangeReason.UNKNOWN, false); | 157 closeReaderPanel(StateChangeReason.UNKNOWN, false); |
| 211 } | 158 } |
| 212 | 159 |
| 213 @Override | 160 @Override |
| 214 public void onDestroyed(Tab tab) { | 161 public void onDestroyed(Tab tab) { |
| 215 if (tab == null) return; | 162 if (tab == null) return; |
| 216 if (tab.getInfoBarContainer() != null) { | 163 |
| 217 tab.getInfoBarContainer().removeObserver(this); | |
| 218 } | |
| 219 // If the panel was not shown for the previous navigation, record it now . | 164 // If the panel was not shown for the previous navigation, record it now . |
| 220 ReaderModeTabInfo info = mTabStatusMap.get(tab.getId()); | 165 ReaderModeTabInfo info = mTabStatusMap.get(tab.getId()); |
| 221 if (info != null && !info.isPanelShowRecorded()) { | 166 if (info != null && !info.isPanelShowRecorded()) { |
| 222 recordPanelVisibilityForNavigation(false); | 167 recordPanelVisibilityForNavigation(false); |
| 223 } | 168 } |
| 224 removeTabState(tab.getId()); | 169 removeTabState(tab.getId()); |
| 225 } | 170 } |
| 226 | 171 |
| 172 @Override | |
| 173 public void onPageLoadStarted(Tab tab, String url) { | |
| 174 // Reader Mode should not pollute the navigation stack. To avoid this, w atch for navigations | |
| 175 // and prepare to remove any that are "chrome-distiller" urls. | |
| 176 NavigationController controller = tab.getWebContents().getNavigationCont roller(); | |
| 177 int index = controller.getLastCommittedEntryIndex(); | |
| 178 NavigationEntry entry = controller.getEntryAtIndex(index); | |
| 179 if (entry == null) return; | |
| 180 | |
| 181 if (DomDistillerUrlUtils.isDistilledPage(entry.getUrl())) { | |
| 182 mShouldRemovePreviousNavigation = true; | |
| 183 mLastDistillerPageIndex = index; | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 @Override | |
| 188 public void onDidFinishNavigation(Tab tab, String url, boolean isInMainFrame , | |
| 189 boolean isErrorPage, boolean hasCommitted, boolean isSameDocument, | |
| 190 boolean isFragmentNavigation, Integer pageTransition, int errorCode, | |
| 191 int httpStatusCode) { | |
| 192 if (!hasCommitted || !mShouldRemovePreviousNavigation || !isInMainFrame) return; | |
| 193 | |
| 194 mShouldRemovePreviousNavigation = false; | |
| 195 NavigationController controller = tab.getWebContents().getNavigationCont roller(); | |
| 196 controller.removeEntryAtIndex(mLastDistillerPageIndex); | |
| 197 } | |
| 198 | |
| 227 /** | 199 /** |
| 228 * Clean up the state associated with a tab. | 200 * Clean up the state associated with a tab. |
| 229 * @param tabId The target tab ID. | 201 * @param tabId The target tab ID. |
| 230 */ | 202 */ |
| 231 private void removeTabState(int tabId) { | 203 private void removeTabState(int tabId) { |
| 232 if (!mTabStatusMap.containsKey(tabId)) return; | 204 if (!mTabStatusMap.containsKey(tabId)) return; |
| 233 ReaderModeTabInfo tabInfo = mTabStatusMap.get(tabId); | 205 ReaderModeTabInfo tabInfo = mTabStatusMap.get(tabId); |
| 234 if (tabInfo.getWebContentsObserver() != null) { | 206 if (tabInfo.getWebContentsObserver() != null) { |
| 235 tabInfo.getWebContentsObserver().destroy(); | 207 tabInfo.getWebContentsObserver().destroy(); |
| 236 } | 208 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 257 if (tab.getWebContents() != null) { | 229 if (tab.getWebContents() != null) { |
| 258 tabInfo.setWebContentsObserver(createWebContentsObserver(tab.getWebC ontents())); | 230 tabInfo.setWebContentsObserver(createWebContentsObserver(tab.getWebC ontents())); |
| 259 if (DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) { | 231 if (DomDistillerUrlUtils.isDistilledPage(tab.getUrl())) { |
| 260 tabInfo.setStatus(STARTED); | 232 tabInfo.setStatus(STARTED); |
| 261 mReaderModePageUrl = tab.getUrl(); | 233 mReaderModePageUrl = tab.getUrl(); |
| 262 closeReaderPanel(StateChangeReason.CONTENT_CHANGED, true); | 234 closeReaderPanel(StateChangeReason.CONTENT_CHANGED, true); |
| 263 } | 235 } |
| 264 // Make sure there is a distillability delegate set on the WebConten ts. | 236 // Make sure there is a distillability delegate set on the WebConten ts. |
| 265 setDistillabilityCallback(tab.getId()); | 237 setDistillabilityCallback(tab.getId()); |
| 266 } | 238 } |
| 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 } | 239 } |
| 315 | 240 |
| 316 // ReaderModeManagerDelegate: | 241 // ReaderModeManagerDelegate: |
| 317 | 242 |
| 318 @Override | 243 @Override |
| 319 public void setReaderModePanel(ReaderModePanel panel) { | |
| 320 mReaderModePanel = panel; | |
| 321 } | |
| 322 | |
| 323 @Override | |
| 324 public ChromeActivity getChromeActivity() { | 244 public ChromeActivity getChromeActivity() { |
| 325 return mChromeActivity; | 245 return mChromeActivity; |
| 326 } | 246 } |
| 327 | 247 |
| 328 @Override | 248 @Override |
| 329 public void onPanelShown() { | 249 public void onPanelShown() { |
| 330 if (mTabModelSelector == null) return; | 250 if (mTabModelSelector == null) return; |
| 331 int tabId = mTabModelSelector.getCurrentTabId(); | 251 int tabId = mTabModelSelector.getCurrentTabId(); |
| 332 | 252 |
| 333 ReaderModeTabInfo info = mTabStatusMap.get(tabId); | 253 ReaderModeTabInfo info = mTabStatusMap.get(tabId); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 344 /** | 264 /** |
| 345 * Record if the panel became visible on the current page. This can be overr idden for testing. | 265 * 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. | 266 * @param visible If the panel was visible at any time. |
| 347 */ | 267 */ |
| 348 protected void recordPanelVisibilityForNavigation(boolean visible) { | 268 protected void recordPanelVisibilityForNavigation(boolean visible) { |
| 349 RecordHistogram.recordBooleanHistogram("DomDistiller.ReaderShownForPageL oad", visible); | 269 RecordHistogram.recordBooleanHistogram("DomDistiller.ReaderShownForPageL oad", visible); |
| 350 } | 270 } |
| 351 | 271 |
| 352 @Override | 272 @Override |
| 353 public void onClosed(StateChangeReason reason) { | 273 public void onClosed(StateChangeReason reason) { |
| 354 if (mReaderModePanel == null || mTabModelSelector == null) return; | 274 if (mTabModelSelector == null) return; |
| 355 | |
| 356 restoreInfobars(); | |
| 357 | 275 |
| 358 // Only dismiss the panel if the close was a result of user interaction. | 276 // Only dismiss the panel if the close was a result of user interaction. |
| 359 if (reason != StateChangeReason.FLING && reason != StateChangeReason.SWI PE | 277 if (reason != StateChangeReason.FLING && reason != StateChangeReason.SWI PE |
|
Theresa
2017/05/11 22:31:19
StateChangeReason doesn't really apply anymore rig
mdjones
2017/05/12 19:13:10
Done.
| |
| 360 && reason != StateChangeReason.CLOSE_BUTTON) { | 278 && reason != StateChangeReason.CLOSE_BUTTON) { |
| 361 return; | 279 return; |
| 362 } | 280 } |
| 363 | 281 |
| 364 // Record close button usage. | 282 RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", fals e); |
| 365 if (reason == StateChangeReason.CLOSE_BUTTON) { | |
| 366 RecordHistogram.recordBooleanHistogram("DomDistiller.BarCloseButtonU sage", | |
| 367 mReaderModePanel.getPanelState() == PanelState.EXPANDED | |
| 368 || mReaderModePanel.getPanelState() == PanelState.MAXIMIZED) ; | |
| 369 } | |
| 370 | 283 |
| 371 int currentTabId = mTabModelSelector.getCurrentTabId(); | 284 int currentTabId = mTabModelSelector.getCurrentTabId(); |
| 372 if (!mTabStatusMap.containsKey(currentTabId)) return; | 285 if (!mTabStatusMap.containsKey(currentTabId)) return; |
| 373 mTabStatusMap.get(currentTabId).setIsDismissed(true); | 286 mTabStatusMap.get(currentTabId).setIsDismissed(true); |
| 374 } | 287 } |
| 375 | 288 |
| 376 @Override | 289 @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() { | 290 public WebContents getBasePageWebContents() { |
| 401 Tab tab = mTabModelSelector.getCurrentTab(); | 291 Tab tab = mTabModelSelector.getCurrentTab(); |
| 402 if (tab == null) return null; | 292 if (tab == null) return null; |
| 403 | 293 |
| 404 return tab.getWebContents(); | 294 return tab.getWebContents(); |
| 405 } | 295 } |
| 406 | 296 |
| 407 @Override | 297 @Override |
| 408 public void closeReaderPanel(StateChangeReason reason, boolean animate) { | 298 public void closeReaderPanel(StateChangeReason reason, boolean animate) { |
| 409 if (mReaderModePanel == null) return; | 299 // TODO(mdjones): Remove this method and dependencies. |
|
Theresa
2017/05/11 22:31:19
InfoBar handles automatically dismissing itself?
mdjones
2017/05/12 19:13:10
In most cases where we want it, yes. That's the ni
| |
| 410 mReaderModePanel.closePanel(reason, animate); | |
| 411 } | 300 } |
| 412 | 301 |
| 413 @Override | 302 @Override |
| 414 public void recordTimeSpentInReader(long timeMs) { | 303 public void recordTimeSpentInReader(long timeMs) { |
| 415 RecordHistogram.recordLongTimesHistogram("DomDistiller.Time.ViewingReade rModePanel", | 304 RecordHistogram.recordLongTimesHistogram("DomDistiller.Time.ViewingReade rModePanel", |
| 416 timeMs, TimeUnit.MILLISECONDS); | 305 timeMs, TimeUnit.MILLISECONDS); |
| 417 } | 306 } |
| 418 | 307 |
| 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 /** | 308 /** |
| 431 * @return True if the keyboard might be showing. This is not 100% accurate; see | 309 * @return True if the keyboard might be showing. This is not 100% accurate; see |
| 432 * UiUtils.isKeyboardShowing(...). | 310 * UiUtils.isKeyboardShowing(...). |
| 433 */ | 311 */ |
| 434 protected boolean isKeyboardShowing() { | 312 protected boolean isKeyboardShowing() { |
| 435 return mChromeActivity != null && UiUtils.isKeyboardShowing(mChromeActiv ity, | 313 return mChromeActivity != null && UiUtils.isKeyboardShowing(mChromeActiv ity, |
| 436 mChromeActivity.findViewById(android.R.id.content)); | 314 mChromeActivity.findViewById(android.R.id.content)); |
| 437 } | 315 } |
| 438 | 316 |
| 439 protected WebContentsObserver createWebContentsObserver(WebContents webConte nts) { | 317 protected WebContentsObserver createWebContentsObserver(WebContents webConte nts) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 456 if (tabInfo == null) return; | 334 if (tabInfo == null) return; |
| 457 | 335 |
| 458 tabInfo.setUrl(url); | 336 tabInfo.setUrl(url); |
| 459 if (DomDistillerUrlUtils.isDistilledPage(url)) { | 337 if (DomDistillerUrlUtils.isDistilledPage(url)) { |
| 460 tabInfo.setStatus(STARTED); | 338 tabInfo.setStatus(STARTED); |
| 461 mReaderModePageUrl = url; | 339 mReaderModePageUrl = url; |
| 462 } | 340 } |
| 463 } | 341 } |
| 464 | 342 |
| 465 @Override | 343 @Override |
| 466 public void didFinishNavigation(String url, boolean isInMainFrame, b oolean isErrorPage, | 344 public void didFinishNavigation(String url, boolean isInMainFrame, b oolean isErrorPage, |
|
Theresa
2017/05/11 22:31:19
When is this called vs onDidFinishNavigation?
mdjones
2017/05/12 19:13:10
Not a big difference, I'll move those functions in
| |
| 467 boolean hasCommitted, boolean isSameDocument, boolean isFrag mentNavigation, | 345 boolean hasCommitted, boolean isSameDocument, boolean isFrag mentNavigation, |
| 468 Integer pageTransition, int errorCode, String errorDescripti on, | 346 Integer pageTransition, int errorCode, String errorDescripti on, |
| 469 int httpStatusCode) { | 347 int httpStatusCode) { |
| 470 // TODO(cjhopman): This should possibly ignore navigations that replace the entry | 348 // TODO(cjhopman): This should possibly ignore navigations that replace the entry |
| 471 // (like those from history.replaceState()). | 349 // (like those from history.replaceState()). |
| 472 if (!hasCommitted || !isInMainFrame || isSameDocument) return; | 350 if (!hasCommitted || !isInMainFrame || isSameDocument) return; |
| 473 if (DomDistillerUrlUtils.isDistilledPage(url)) return; | |
| 474 | 351 |
| 475 // Make sure the tab was not destroyed. | 352 // Make sure the tab was not destroyed. |
| 476 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); | 353 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); |
| 477 if (tabInfo == null) return; | 354 if (tabInfo == null) return; |
| 478 | 355 |
| 479 tabInfo.setStatus(POSSIBLE); | 356 tabInfo.setStatus(POSSIBLE); |
| 480 if (!TextUtils.equals(url, | 357 if (!TextUtils.equals(url, |
| 481 DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl( | 358 DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl( |
| 482 mReaderModePageUrl))) { | 359 mReaderModePageUrl))) { |
| 483 tabInfo.setStatus(NOT_POSSIBLE); | 360 tabInfo.setStatus(NOT_POSSIBLE); |
| 484 mIsUmaRecorded = false; | 361 mIsUmaRecorded = false; |
| 485 } | 362 } |
| 486 mReaderModePageUrl = null; | 363 mReaderModePageUrl = null; |
| 487 | 364 |
| 488 if (tabInfo.getStatus() != POSSIBLE) { | 365 if (tabInfo.getStatus() != POSSIBLE) { |
| 489 closeReaderPanel(StateChangeReason.UNKNOWN, false); | 366 closeReaderPanel(StateChangeReason.UNKNOWN, false); |
| 490 } else { | 367 } else { |
| 491 requestReaderPanelShow(StateChangeReason.UNKNOWN); | 368 tryShowingInfoBar(); |
| 492 } | 369 } |
| 493 } | 370 } |
| 494 | 371 |
| 495 @Override | 372 @Override |
| 496 public void navigationEntryCommitted() { | 373 public void navigationEntryCommitted() { |
| 497 // Make sure the tab was not destroyed. | 374 // Make sure the tab was not destroyed. |
| 498 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); | 375 ReaderModeTabInfo tabInfo = mTabStatusMap.get(readerTabId); |
| 499 if (tabInfo == null) return; | 376 if (tabInfo == null) return; |
| 500 // Reset closed state of reader mode in this tab once we know a navigation is | 377 // Reset closed state of reader mode in this tab once we know a navigation is |
| 501 // happening. | 378 // happening. |
| 502 tabInfo.setIsDismissed(false); | 379 tabInfo.setIsDismissed(false); |
| 503 | 380 |
| 504 // If the panel was not shown for the previous navigation, recor d it now. | 381 // If the panel was not shown for the previous navigation, recor d it now. |
| 505 Tab curTab = mTabModelSelector.getTabById(readerTabId); | 382 Tab curTab = mTabModelSelector.getTabById(readerTabId); |
| 506 if (curTab != null && !curTab.isNativePage() && !curTab.isBeingR estored()) { | 383 if (curTab != null && !curTab.isNativePage() && !curTab.isBeingR estored()) { |
| 507 recordPanelVisibilityForNavigation(false); | 384 recordPanelVisibilityForNavigation(false); |
| 508 } | 385 } |
| 509 tabInfo.setIsPanelShowRecorded(false); | 386 tabInfo.setIsPanelShowRecorded(false); |
| 510 } | 387 } |
| 511 }; | 388 }; |
| 512 } | 389 } |
| 513 | 390 |
| 514 /** | 391 /** |
| 515 * This is a wrapper for "requestPanelShow" that checks if reader mode is po ssible before | 392 * Try showing the reader mode infobar. |
| 516 * showing. | |
| 517 * @param reason The reason the panel is requesting to be shown. | |
| 518 */ | 393 */ |
| 519 protected void requestReaderPanelShow(StateChangeReason reason) { | 394 protected void tryShowingInfoBar() { |
| 520 if (mTabModelSelector == null) return; | 395 if (mTabModelSelector == null) return; |
| 521 | 396 |
| 522 int currentTabId = mTabModelSelector.getCurrentTabId(); | 397 int currentTabId = mTabModelSelector.getCurrentTabId(); |
| 523 if (currentTabId == Tab.INVALID_TAB_ID) return; | 398 if (currentTabId == Tab.INVALID_TAB_ID) return; |
| 524 | 399 |
| 525 // Test if the user is requesting the desktop site. Ignore this if disti ller is set to | 400 // Test if the user is requesting the desktop site. Ignore this if disti ller is set to |
| 526 // ALWAYS_TRUE. | 401 // ALWAYS_TRUE. |
| 527 boolean usingRequestDesktopSite = getBasePageWebContents() != null | 402 boolean usingRequestDesktopSite = getBasePageWebContents() != null |
| 528 && getBasePageWebContents().getNavigationController().getUseDesk topUserAgent() | 403 && getBasePageWebContents().getNavigationController().getUseDesk topUserAgent() |
| 529 && !mIsReaderHeuristicAlwaysTrue; | 404 && !mIsReaderHeuristicAlwaysTrue; |
| 530 | 405 |
| 531 if (mReaderModePanel == null || !mTabStatusMap.containsKey(currentTabId) | 406 if (!mTabStatusMap.containsKey(currentTabId) || usingRequestDesktopSite |
| 532 || usingRequestDesktopSite | |
| 533 || mTabStatusMap.get(currentTabId).getStatus() != POSSIBLE | 407 || mTabStatusMap.get(currentTabId).getStatus() != POSSIBLE |
| 534 || mTabStatusMap.get(currentTabId).isDismissed() | 408 || mTabStatusMap.get(currentTabId).isDismissed() |
| 535 || mIsInfoBarContainerShown | |
| 536 || mIsFindToolbarShowing | |
| 537 || mIsFullscreenModeEntered | |
| 538 || mIsKeyboardShowing | |
| 539 || AccessibilityUtil.isAccessibilityEnabled()) { | 409 || AccessibilityUtil.isAccessibilityEnabled()) { |
| 540 return; | 410 return; |
| 541 } | 411 } |
| 542 | 412 |
| 543 mReaderModePanel.requestPanelShow(reason); | 413 ReaderModeInfoBar.showReaderModeInfoBar(mTabModelSelector.getCurrentTab( ), this); |
| 544 } | 414 } |
| 545 | 415 |
| 546 /** | 416 /** |
| 417 * Navigate the current tab to a Reader Mode URL. | |
| 418 */ | |
| 419 public void navigateToReaderMode() { | |
| 420 RecordHistogram.recordBooleanHistogram("DomDistiller.InfoBarUsage", true ); | |
| 421 | |
| 422 WebContents baseWebContents = getBasePageWebContents(); | |
| 423 if (baseWebContents == null || mChromeActivity == null || mTabModelSelec tor == null) return; | |
| 424 | |
| 425 String url = baseWebContents.getUrl(); | |
| 426 if (url == null) return; | |
| 427 | |
| 428 DomDistillerTabUtils.distillCurrentPageAndView(getBasePageWebContents()) ; | |
| 429 } | |
| 430 | |
| 431 /** | |
| 547 * Open a link from the panel in a new tab. | 432 * Open a link from the panel in a new tab. |
| 548 * @param url The URL to load. | 433 * @param url The URL to load. |
| 549 */ | 434 */ |
| 550 @Override | 435 @Override |
| 551 public void createNewTab(String url) { | 436 public void createNewTab(String url) { |
| 552 if (mChromeActivity == null) return; | 437 if (mChromeActivity == null) return; |
| 553 | 438 |
| 554 Tab currentTab = mTabModelSelector.getCurrentTab(); | 439 Tab currentTab = mTabModelSelector.getCurrentTab(); |
| 555 if (currentTab == null) return; | 440 if (currentTab == null) return; |
| 556 | 441 |
| 557 TabCreatorManager.TabCreator tabCreator = | 442 TabCreatorManager.TabCreator tabCreator = |
| 558 mChromeActivity.getTabCreator(currentTab.isIncognito()); | 443 mChromeActivity.getTabCreator(currentTab.isIncognito()); |
| 559 if (tabCreator == null) return; | 444 if (tabCreator == null) return; |
| 560 | 445 |
| 561 tabCreator.createNewTab(new LoadUrlParams(url, PageTransition.LINK), | 446 tabCreator.createNewTab(new LoadUrlParams(url, PageTransition.LINK), |
| 562 TabModel.TabLaunchType.FROM_LINK, mChromeActivity.getActivityTab ()); | 447 TabModel.TabLaunchType.FROM_LINK, mChromeActivity.getActivityTab ()); |
| 563 } | 448 } |
| 564 | 449 |
| 565 /** | 450 /** |
| 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 | 451 * Set the callback for updating reader mode status based on whether or not the page should |
| 583 * be viewed in reader mode. | 452 * be viewed in reader mode. |
| 584 * @param tabId The ID of the tab having its callback set. | 453 * @param tabId The ID of the tab having its callback set. |
| 585 */ | 454 */ |
| 586 private void setDistillabilityCallback(final int tabId) { | 455 private void setDistillabilityCallback(final int tabId) { |
| 587 if (tabId == Tab.INVALID_TAB_ID || mTabStatusMap.get(tabId).isCallbackSe t()) { | 456 if (tabId == Tab.INVALID_TAB_ID || mTabStatusMap.get(tabId).isCallbackSe t()) { |
| 588 return; | 457 return; |
| 589 } | 458 } |
| 590 | 459 |
| 591 if (mTabModelSelector == null) return; | 460 if (mTabModelSelector == null) return; |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 610 // destroyed so that this never happens. | 479 // destroyed so that this never happens. |
| 611 if (readerTab == null || tabInfo == null) return; | 480 if (readerTab == null || tabInfo == null) return; |
| 612 | 481 |
| 613 // Make sure the page didn't navigate while waiting for a response. | 482 // Make sure the page didn't navigate while waiting for a response. |
| 614 if (!readerTab.getUrl().equals(tabInfo.getUrl())) return ; | 483 if (!readerTab.getUrl().equals(tabInfo.getUrl())) return ; |
| 615 | 484 |
| 616 if (isDistillable) { | 485 if (isDistillable) { |
| 617 tabInfo.setStatus(POSSIBLE); | 486 tabInfo.setStatus(POSSIBLE); |
| 618 // The user may have changed tabs. | 487 // The user may have changed tabs. |
| 619 if (tabId == mTabModelSelector.getCurrentTabId()) { | 488 if (tabId == mTabModelSelector.getCurrentTabId()) { |
| 620 // TODO(mdjones): Add reason DISTILLER_STATE_CHA NGE. | 489 tryShowingInfoBar(); |
| 621 requestReaderPanelShow(StateChangeReason.UNKNOWN ); | |
| 622 } | 490 } |
| 623 } else { | 491 } else { |
| 624 tabInfo.setStatus(NOT_POSSIBLE); | 492 tabInfo.setStatus(NOT_POSSIBLE); |
| 625 } | 493 } |
| 626 if (!mIsUmaRecorded && (tabInfo.getStatus() == POSSIBLE || isLast)) { | 494 if (!mIsUmaRecorded && (tabInfo.getStatus() == POSSIBLE || isLast)) { |
| 627 mIsUmaRecorded = true; | 495 mIsUmaRecorded = true; |
| 628 RecordHistogram.recordBooleanHistogram( | 496 RecordHistogram.recordBooleanHistogram( |
| 629 "DomDistiller.PageDistillable", | 497 "DomDistiller.PageDistillable", |
| 630 tabInfo.getStatus() == POSSIBLE); | 498 tabInfo.getStatus() == POSSIBLE); |
| 631 } | 499 } |
| 632 } | 500 } |
| 633 }); | 501 }); |
| 634 mTabStatusMap.get(tabId).setIsCallbackSet(true); | 502 mTabStatusMap.get(tabId).setIsCallbackSet(true); |
| 635 } | 503 } |
| 636 | 504 |
| 637 /** | 505 /** |
| 638 * @return Whether Reader mode and its new UI are enabled. | 506 * @return Whether Reader mode and its new UI are enabled. |
| 639 * @param context A context | 507 * @param context A context |
| 640 */ | 508 */ |
| 641 public static boolean isEnabled(Context context) { | 509 public static boolean isEnabled(Context context) { |
| 642 if (context == null) return false; | 510 if (context == null) return false; |
| 643 | 511 |
| 644 boolean enabled = CommandLine.getInstance().hasSwitch(ChromeSwitches.ENA BLE_DOM_DISTILLER) | 512 boolean enabled = CommandLine.getInstance().hasSwitch(ChromeSwitches.ENA BLE_DOM_DISTILLER) |
| 645 && !CommandLine.getInstance().hasSwitch( | 513 && !CommandLine.getInstance().hasSwitch( |
| 646 ChromeSwitches.DISABLE_READER_MODE_BOTTOM_BAR) | 514 ChromeSwitches.DISABLE_READER_MODE_BOTTOM_BAR) |
| 647 && !DeviceFormFactor.isTablet(context) | 515 && DomDistillerTabUtils.isDistillerHeuristicsEnabled(); |
| 648 && DomDistillerTabUtils.isDistillerHeuristicsEnabled() | |
| 649 && !SysUtils.isLowEndDevice(); | |
| 650 return enabled; | 516 return enabled; |
| 651 } | 517 } |
| 652 } | 518 } |
| OLD | NEW |