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

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModePanel.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModePanel.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModePanel.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModePanel.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa4c6cce7d68fe7ba640a8bec8464de17e519902
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/dom_distiller/ReaderModePanel.java
@@ -0,0 +1,682 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.dom_distiller;
+
+import static org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.AnimatableAnimation.createAnimation;
+
+import android.content.Context;
+
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ContentViewUtil;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
+import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEventFilter.ScrollDirection;
+import org.chromium.chrome.browser.dom_distiller.ReaderModeButtonView.ReaderModeButtonViewDelegate;
+import org.chromium.chrome.browser.tab.ChromeTab;
+import org.chromium.chrome.browser.util.MathUtils;
+import org.chromium.content.browser.ContentView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.content_public.common.TopControlsState;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * Manages UI effects for reader mode including hiding and showing the
+ * reader mode and reader mode preferences toolbar icon and hiding the
+ * top controls when a reader mode page has finished loading.
+ *
+ * TODO(aruslan): combine with ContextualSearchPanel.
+ */
+public class ReaderModePanel implements ChromeAnimation.Animatable<ReaderModePanel.Property> {
+ // TODO(aruslan): pull this from the FullscreenManager.
+ private static final float TOOLBAR_HEIGHT_DP = 56.0f;
+
+ private static final float PANEL_HEIGHT_DP = TOOLBAR_HEIGHT_DP;
+ private static final float SHADOW_HEIGHT_DP = 4.0f;
+ private static final float MINIMAL_BORDER_X_DP = 4.0f;
+ private static final float DARKEN_LAYOUTTAB_BRIGHTNESS = 0.3f;
+ private static final float MAX_LAYOUTTAB_DISPLACEMENT = 3.0f * TOOLBAR_HEIGHT_DP;
+
+ private static final float SNAP_BACK_THRESHOLD = 0.3f;
+ private static final long BASE_ANIMATION_DURATION_MS = 500;
+
+ /**
+ * Panel's host interface.
+ */
+ public interface ReaderModePanelHost {
+ /**
+ * @return Whether the reader mode button should be animated.
+ */
+ boolean allowReaderModeButtonAnimation();
+
+ /**
+ * @return Reader mode header background color.
+ */
+ int getReaderModeHeaderBackgroundColor();
+
+ /**
+ * @return One of ReaderModeManager.POSSIBLE, NOT_POSSIBLE, STARTED constants.
+ */
+ int getReaderModeStatus();
+
+ /**
+ * @return An associated tab.
+ */
+ Tab getTab();
+
+ /**
+ * @param X X-coordinate in dp
+ * @param Y Y-coordinate in dp
+ * @return Whether a given coordinates are within the bounds of the "dismiss" button
+ */
+ public boolean isInsideDismissButton(float x, float y);
+ }
+
+ /**
+ * Layout integration interface.
+ */
+ public interface ReaderModePanelLayoutDelegate {
+ /**
+ * Requests a next update to refresh the transforms and changing properties.
+ */
+ void requestUpdate();
+
+ /**
+ * Sets the brightness of the LayoutTab to a given value.
+ * @param v Brightness
+ */
+ void setLayoutTabBrightness(float v);
+
+ /**
+ * Sets the Y offset of the LayoutTab to a given value.
+ * @param v Y-offset in dp
+ */
+ void setLayoutTabY(float v);
+ }
+
+ /**
+ * Properties that can be animated by using a
+ * {@link org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable}.
+ */
+ public enum Property {
+ /**
+ * Parametric vertical slider from
+ * -1.0 (panel is out of screen) to
+ * 0.0 (panel is on screen) to
+ * 1.0 (panel covers the entire screen)
+ */
+ SLIDING_T,
+ /**
+ * Horizontal slider, offset in dp
+ */
+ X,
+ }
+
+ private float mSlidingT;
+ private float mX;
+
+ private ScrollDirection mSwipeDirection; // set in swipeStarted
+ private float mInitialPanelDistanceFromBottom; // distance from the bottom at swipeStarted
+ private float mInitialX; // X at swipeStarted
+
+ /**
+ * The animation set.
+ */
+ private ChromeAnimation<ChromeAnimation.Animatable<?>> mLayoutAnimations;
+
+ private boolean mIsReaderModePanelHidden;
+ private boolean mIsReaderModePanelDismissed;
+ private ContentViewCore mDistilledContentViewCore;
+ private WebContentsObserver mDistilledContentObserver;
+ private boolean mDidFirstNonEmptyDistilledPaint;
+ private ReaderModePanelLayoutDelegate mLayoutDelegate;
+
+ private float mLayoutWidth;
+ private float mLayoutHeight;
+ private boolean mIsToolbarShowing;
+ private float mDpToPx;
+
+ /**
+ * The {@link ReaderModePanelHost} used to get reader mode status and the associated tab.
+ */
+ private final ReaderModePanelHost mReaderModeHost;
+
+ /**
+ * Non-animated button support.
+ */
+ private boolean mAllowAnimatedButton;
+ private ReaderModeButtonView mReaderModeButtonView;
+
+ public ReaderModePanel(ReaderModePanelHost readerModeHost) {
+ mReaderModeHost = readerModeHost;
+ mAllowAnimatedButton = mReaderModeHost.allowReaderModeButtonAnimation();
+
+ mLayoutWidth = 0.0f;
+ mLayoutHeight = 0.0f;
+ mDpToPx = 1.0f;
+
+ mSlidingT = -1.0f;
+ mX = 0.0f;
+ }
+
+ /**
+ * Destroys the panel and associated resources.
+ */
+ public void onDestroy() {
+ mLayoutAnimations = null;
+ hideButtonBar();
+ }
+
+ /**
+ * Set the layout delegate.
+ * @param layoutDelegate A {@link ReaderModePanelLayoutDelegate} to call.
+ */
+ public void setLayoutDelegate(ReaderModePanelLayoutDelegate layoutDelegate) {
+ mLayoutDelegate = layoutDelegate;
+ requestUpdate();
+ }
+
+ // ChromeAnimation.Animatable<Property>:
+
+ private void setSlidingT(float val) {
+ mSlidingT = val;
+ if (mLayoutDelegate != null) {
+ mLayoutDelegate.setLayoutTabBrightness(getTabBrightness());
+ mLayoutDelegate.setLayoutTabY(getTabYOffset());
+ }
+ }
+
+ @Override
+ public void setProperty(Property prop, float val) {
+ switch (prop) {
+ case SLIDING_T:
+ setSlidingT(val);
+ break;
+ case X:
+ mX = val;
+ break;
+ }
+ }
+
+ private static float clamp(float val, float lower, float higher) {
+ return val < lower ? lower : (val > higher ? higher : val);
+ }
+
+ private static float interp(float factor, float start, float end) {
+ return start + clamp(factor, 0.0f, 1.0f) * (end - start);
+ }
+
+ private float getPanelDistanceFromBottom() {
+ if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, 0.0f, PANEL_HEIGHT_DP);
+ return PANEL_HEIGHT_DP + interp(mSlidingT, 0.0f, getFullscreenHeight());
+ }
+
+ private float getSlidingTForPanelDistanceFromBottom(float distanceFromBottom) {
+ if (distanceFromBottom >= PANEL_HEIGHT_DP) {
+ return interp(
+ (distanceFromBottom - PANEL_HEIGHT_DP) / getFullscreenHeight(),
+ 0.0f, 1.0f);
+ }
+ return interp(
+ (PANEL_HEIGHT_DP - distanceFromBottom) / PANEL_HEIGHT_DP,
+ 0.0f, -1.0f);
+ }
+
+ private float getDistilledContentDistanceFromBottom() {
+ if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, -PANEL_HEIGHT_DP, 0.0f);
+ return interp(mSlidingT, 0.0f, getFullscreenHeight());
+ }
+
+ private static float snapBackSlidingT(float v) {
+ // We snap asymmetrically: 30% is enough to get it opened, but 70% is necessary to dismiss.
+ v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= SNAP_BACK_THRESHOLD ? v : 0.0f);
+ return Math.signum(v);
+ }
+
+ private static float snapBackX(float v) {
+ // Horizontally we snap symmetrically: more than 70% to each side to dismiss.
+ v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= 1.0f - SNAP_BACK_THRESHOLD ? v : 0.0f);
+ return Math.signum(v);
+ }
+
+ // Gesture handling:
+
+ /**
+ * @param direction Swipe direction to test
+ * @return Whether the swipe in a given direction is enabled
+ */
+ public boolean isSwipeEnabled(ScrollDirection direction) {
+ return !isAnimating();
+ }
+
+ /**
+ * Called when the swipe is started.
+ * @param direction Swipe direction
+ * @param x X-coordinate of the starting point in dp
+ * @param y Y-coordinate of the starting point in dp
+ */
+ public void swipeStarted(ScrollDirection direction, float x, float y) {
+ if (isAnimating()) return;
+
+ mSwipeDirection = direction;
+ mInitialPanelDistanceFromBottom = getPanelDistanceFromBottom();
+ mX = getX();
+
+ if (mSwipeDirection == ScrollDirection.UP) activatePreviewOfDistilledMode();
+
+ requestUpdate();
+ }
+
+ /**
+ * Called when the swipe is continued.
+ * @param tx X-offset since the start of the swipe in dp
+ * @param ty Y-offset since the start of the swipe in dp
+ */
+ public void swipeUpdated(float x, float y, float dx, float dy, float tx, float ty) {
+ if (isAnimating()) return;
+
+ if (mSwipeDirection == ScrollDirection.LEFT || mSwipeDirection == ScrollDirection.RIGHT) {
+ setProperty(ReaderModePanel.Property.X, clamp(mInitialX + tx,
+ -mLayoutWidth + MINIMAL_BORDER_X_DP, mLayoutWidth - MINIMAL_BORDER_X_DP));
+ } else {
+ setProperty(ReaderModePanel.Property.SLIDING_T,
+ getSlidingTForPanelDistanceFromBottom(mInitialPanelDistanceFromBottom - ty));
+ }
+ requestUpdate();
+ }
+
+ /**
+ * Called when the swipe is finished.
+ */
+ public void swipeFinished() {
+ if (isAnimating()) return;
+
+ final float snappedX = snapBackX(mX / mLayoutWidth) * mLayoutWidth;
+ final float snappedSlidingT = snapBackSlidingT(mSlidingT);
+ if (snappedX <= -mLayoutWidth || snappedX >= mLayoutWidth) dismissButtonBar();
+ if (snappedSlidingT < 0.0f) dismissButtonBar();
+
+ animateTo(snappedX, snappedSlidingT, true);
+ }
+
+ // Panel layout handling:
+
+ /**
+ * @return Whether the panel should be shown.
+ */
+ public boolean isShowing() {
+ return isPanelWithinScreenBounds() || isAnimating() || mDistilledContentViewCore != null;
+ }
+
+ /**
+ * @return Whether the panel is within screen bounds.
+ */
+ private boolean isPanelWithinScreenBounds() {
+ return mSlidingT > -1.0f;
+ }
+
+ /**
+ * @return The fullscreen height.
+ */
+ private float getFullscreenHeight() {
+ return mLayoutHeight + TOOLBAR_HEIGHT_DP;
+ }
+
+ public float getFullscreenY(float y) {
+ if (mIsToolbarShowing) y += TOOLBAR_HEIGHT_DP * mDpToPx;
+ return y;
+ }
+
+ public float getPanelY() {
+ return getFullscreenHeight() - getPanelDistanceFromBottom() - SHADOW_HEIGHT_DP;
+ }
+
+ public float getDistilledContentY() {
+ return getFullscreenHeight() - getDistilledContentDistanceFromBottom() - SHADOW_HEIGHT_DP;
+ }
+
+ public float getWidth() {
+ return mLayoutWidth;
+ }
+
+ public float getPanelHeight() {
+ return getPanelDistanceFromBottom();
+ }
+
+ public float getMarginTop() {
+ return SHADOW_HEIGHT_DP;
+ }
+
+ public float getDistilledHeight() {
+ return getDistilledContentDistanceFromBottom();
+ }
+
+ public float getX() {
+ return mX;
+ }
+
+ public float getTextOpacity() {
+ return interp(mSlidingT, 1.0f, 0.0f);
+ }
+
+ public float getTabBrightness() {
+ return interp(mSlidingT, 1.0f, DARKEN_LAYOUTTAB_BRIGHTNESS);
+ }
+
+ public float getTabYOffset() {
+ return interp(mSlidingT, 0.0f, -MAX_LAYOUTTAB_DISPLACEMENT);
+ }
+
+ /**
+ * @param currentOffset The current top controls offset in dp.
+ * @return {@link Float#NaN} if no offset should be used, or a value in dp
+ * if the top controls offset should be overridden.
+ */
+ public float getTopControlsOffset(float currentOffsetDp) {
+ if (mSlidingT <= 0.0f) return Float.NaN;
+ return MathUtils.clamp(getTabYOffset(), -TOOLBAR_HEIGHT_DP, Math.min(currentOffsetDp, 0f));
+ }
+
+
+ public ContentViewCore getDistilledContentViewCore() {
+ return mDistilledContentViewCore;
+ }
+
+ public boolean didFirstNonEmptyDistilledPaint() {
+ return mDidFirstNonEmptyDistilledPaint;
+ }
+
+ public int getReaderModeHeaderBackgroundColor() {
+ return mReaderModeHost.getReaderModeHeaderBackgroundColor();
+ }
+
+ /**
+ * Called when the size of the view has changed.
+ *
+ * @param width The new width in dp.
+ * @param height The new width in dp.
+ * @param isToolbarShowing Whether the Toolbar is showing.
+ * @param dpToPx Multipler to convert from dp to pixels.
+ */
+ public void onSizeChanged(float width, float height, boolean isToolbarShowing, float dpToPx) {
+ mLayoutWidth = width;
+ mLayoutHeight = height;
+ mIsToolbarShowing = isToolbarShowing;
+ mDpToPx = dpToPx;
+ }
+
+ // Layout integration:
+
+ /**
+ * Requests a new frame to be updated and rendered.
+ */
+ private void requestUpdate() {
+ if (mLayoutDelegate != null) mLayoutDelegate.requestUpdate();
+ }
+
+ // Animation handling:
+
+ /**
+ * @return Whether a panel animation is in progress.
+ */
+ private boolean isAnimating() {
+ return mLayoutAnimations != null && !mLayoutAnimations.finished();
+ }
+
+ /**
+ * Animates to a given target value.
+ * @param targetX A target value for the X parameter
+ * @param targetSlidingT A target value for the SlidingT parameter
+ */
+ private void animateTo(float targetX, float targetSlidingT, boolean animate) {
+ if (targetSlidingT > 0.0f) activatePreviewOfDistilledMode();
+
+ if (isAnimating()) {
+ mLayoutAnimations.cancel(this, Property.SLIDING_T);
+ mLayoutAnimations.cancel(this, Property.X);
+ }
+ if (mLayoutAnimations == null || mLayoutAnimations.finished()) {
+ mLayoutAnimations = new ChromeAnimation<Animatable<?>>();
+ }
+
+ mLayoutAnimations.add(createAnimation(
+ this, Property.SLIDING_T, mSlidingT, targetSlidingT,
+ BASE_ANIMATION_DURATION_MS, 0, false,
+ ChromeAnimation.getDecelerateInterpolator()));
+ mLayoutAnimations.add(createAnimation(
+ this, Property.X, mX, targetX,
+ BASE_ANIMATION_DURATION_MS, 0, false,
+ ChromeAnimation.getDecelerateInterpolator()));
+ mLayoutAnimations.start();
+
+ if (!animate) mLayoutAnimations.updateAndFinish();
+ requestUpdate();
+ }
+
+ /**
+ * Steps the animation forward and updates all the animated values.
+ * @param time The current time of the app in ms.
+ * @param jumpToEnd Whether to finish the animation.
+ * @return Whether the animation was finished.
+ */
+ public boolean onUpdateAnimation(long time, boolean jumpToEnd) {
+ boolean finished = true;
+ if (mLayoutAnimations != null) {
+ if (jumpToEnd) {
+ finished = mLayoutAnimations.finished();
+ mLayoutAnimations.updateAndFinish();
+ } else {
+ finished = mLayoutAnimations.update(time);
+ }
+
+ if (finished || jumpToEnd) {
+ mLayoutAnimations = null;
+ onAnimationFinished();
+ }
+ requestUpdate();
+ }
+ return finished;
+ }
+
+ /**
+ * Called when layout-specific actions are needed after the animation finishes.
+ */
+ private void onAnimationFinished() {
+ if (mSlidingT >= 1.0f) enterDistilledMode();
+ updateBottomButtonBar();
+ }
+
+ // Gesture handling:
+
+ /**
+ * @param y The y coordinate in dp.
+ * @return Whether the given |y| coordinate is inside the Reader mode area.
+ */
+ public boolean isYCoordinateInsideReaderModePanel(float y) {
+ return y >= getPanelY() || y >= getDistilledContentY();
+ }
+
+ /**
+ * Handles a click in the panel area.
+ * @param x X-coordinate in dp
+ * @param y Y-coordinate in dp
+ */
+ public void handleClick(long time, float x, float y) {
+ if (mReaderModeHost.isInsideDismissButton(x * mDpToPx + mX, PANEL_HEIGHT_DP / 2)) {
+ dismissButtonBar();
+ return;
+ }
+
+ animateTo(mX, 1.0f, true);
+ }
+
+ private void nonAnimatedUpdateButtomButtonBar() {
+ final int status = mReaderModeHost.getReaderModeStatus();
+ final Tab tab = mReaderModeHost.getTab();
+
+ if (mReaderModeButtonView != null
+ && (status != ReaderModeManager.POSSIBLE || mIsReaderModePanelHidden
+ || mIsReaderModePanelDismissed)) {
+ mReaderModeButtonView.dismiss(true);
+ mReaderModeButtonView = null;
+ return;
+ }
+ if (mReaderModeButtonView == null
+ && (status == ReaderModeManager.POSSIBLE && !mIsReaderModePanelHidden
+ && !mIsReaderModePanelDismissed)) {
+ mReaderModeButtonView = ReaderModeButtonView.create(tab.getContentViewCore(),
+ new ReaderModeButtonViewDelegate() {
+ @Override
+ public void onSwipeAway() {
+ dismissButtonBar();
+ }
+
+ @Override
+ public void onClick() {
+ nonAnimatedEnterDistilledMode();
+ }
+ });
+ }
+ }
+
+ /**
+ * Updates the visibility of the reader mode button bar as required.
+ */
+ public void updateBottomButtonBar() {
+ if (!mAllowAnimatedButton) {
+ nonAnimatedUpdateButtomButtonBar();
+ return;
+ }
+
+ if (isAnimating()) return;
+
+ final int status = mReaderModeHost.getReaderModeStatus();
+ if (isPanelWithinScreenBounds()
+ && (status != ReaderModeManager.POSSIBLE
+ || mIsReaderModePanelHidden || mIsReaderModePanelDismissed)) {
+ animateTo(0.0f, -1.0f, true);
+ destroyDistilledContentViewCore();
+ requestUpdate();
+ return;
+ }
+
+ if (!isPanelWithinScreenBounds()
+ && (status == ReaderModeManager.POSSIBLE
+ && !mIsReaderModePanelHidden && !mIsReaderModePanelDismissed)) {
+ animateTo(0.0f, 0.0f, true);
+ requestUpdate();
+ return;
+ }
+ }
+
+ private static ContentViewCore createDistillerContentViewCore(
+ Context context, WindowAndroid windowAndroid) {
+ ContentViewCore cvc = new ContentViewCore(context);
+ ContentView cv = new ContentView(context, cvc);
+ cvc.initialize(cv, cv, ContentViewUtil.createWebContents(false, true), windowAndroid);
+ return cvc;
+ }
+
+ /**
+ * Prepares the distilled mode.
+ */
+ public void activatePreviewOfDistilledMode() {
+ if (mDistilledContentViewCore != null) return;
+
+ mDidFirstNonEmptyDistilledPaint = false;
+ mDistilledContentViewCore = createDistillerContentViewCore(
+ mReaderModeHost.getTab().getContentViewCore().getContext(),
+ mReaderModeHost.getTab().getWindowAndroid());
+ mDistilledContentObserver = new WebContentsObserver(
+ mDistilledContentViewCore.getWebContents()) {
+ @Override
+ public void didFirstVisuallyNonEmptyPaint() {
+ super.didFirstVisuallyNonEmptyPaint();
+ mDidFirstNonEmptyDistilledPaint = true;
+ }
+ };
+ mReaderModeHost.getTab().attachOverlayContentViewCore(
+ mDistilledContentViewCore, true, false);
+ DomDistillerTabUtils.distillAndView(
+ mReaderModeHost.getTab().getContentViewCore().getWebContents(),
+ mDistilledContentViewCore.getWebContents());
+ mDistilledContentViewCore.onShow();
+
+ mReaderModeHost.getTab().updateTopControlsState(TopControlsState.BOTH, false);
+ }
+
+ private void nonAnimatedEnterDistilledMode() {
+ RecordUserAction.record("DomDistiller_DistilledPageOpened");
+ DomDistillerTabUtils.distillCurrentPageAndView(mReaderModeHost.getTab().getWebContents());
+ mReaderModeHost.getTab().updateTopControlsState(TopControlsState.SHOWN, false);
+ nonAnimatedUpdateButtomButtonBar();
+ }
+
+ private void enterDistilledMode() {
+ RecordUserAction.record("DomDistiller_DistilledPageOpened");
+ mSlidingT = -1.0f;
+ requestUpdate();
+
+ mReaderModeHost.getTab().updateTopControlsState(TopControlsState.HIDDEN, false);
+ DomDistillerTabUtils.distillCurrentPageAndView(mReaderModeHost.getTab().getWebContents());
+ destroyDistilledContentViewCore();
+ if (mLayoutDelegate != null) {
+ mLayoutDelegate.setLayoutTabBrightness(1.0f);
+ mLayoutDelegate.setLayoutTabY(0.0f);
+ }
+
+ updateBottomButtonBar();
+ }
+
+ private void destroyDistilledContentViewCore() {
+ if (mDistilledContentViewCore == null) return;
+
+ mDistilledContentObserver.destroy();
+ mDistilledContentObserver = null;
+ mReaderModeHost.getTab().detachOverlayContentViewCore(mDistilledContentViewCore);
+ mDistilledContentViewCore.destroy();
+ mDistilledContentViewCore = null;
+ }
+
+ /**
+ * Hides the reader mode button bar if shown.
+ */
+ public void hideButtonBar() {
+ if (mIsReaderModePanelHidden) return;
+
+ mIsReaderModePanelHidden = true;
+ updateBottomButtonBar();
+ }
+
+ /**
+ * Dismisses the reader mode button bar if shown.
+ */
+ public void dismissButtonBar() {
+ if (mIsReaderModePanelDismissed) return;
+
+ mIsReaderModePanelDismissed = true;
+ updateBottomButtonBar();
+ }
+
+ /**
+ * Shows the reader mode button bar if necessary.
+ */
+ public void unhideButtonBar() {
+ mIsReaderModePanelHidden = false;
+ updateBottomButtonBar();
+ }
+
+ /**
+ * @param tab A {@link Tab}.
+ * @return The panel associated with a given Tab.
+ */
+ public static ReaderModePanel getReaderModePanel(Tab tab) {
+ if (!(tab instanceof ChromeTab)) return null;
+ ReaderModeManager manager = ((ChromeTab) tab).getReaderModeManager();
+ if (manager == null) return null;
+ return manager.getReaderModePanel();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698