OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.dom_distiller; |
| 6 |
| 7 import static org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Ani
matableAnimation.createAnimation; |
| 8 |
| 9 import android.content.Context; |
| 10 |
| 11 import org.chromium.base.metrics.RecordUserAction; |
| 12 import org.chromium.chrome.browser.ContentViewUtil; |
| 13 import org.chromium.chrome.browser.Tab; |
| 14 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation; |
| 15 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable
; |
| 16 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent
Filter.ScrollDirection; |
| 17 import org.chromium.chrome.browser.dom_distiller.ReaderModeButtonView.ReaderMode
ButtonViewDelegate; |
| 18 import org.chromium.chrome.browser.tab.ChromeTab; |
| 19 import org.chromium.chrome.browser.util.MathUtils; |
| 20 import org.chromium.content.browser.ContentView; |
| 21 import org.chromium.content.browser.ContentViewCore; |
| 22 import org.chromium.content_public.browser.WebContentsObserver; |
| 23 import org.chromium.content_public.common.TopControlsState; |
| 24 import org.chromium.ui.base.WindowAndroid; |
| 25 |
| 26 /** |
| 27 * Manages UI effects for reader mode including hiding and showing the |
| 28 * reader mode and reader mode preferences toolbar icon and hiding the |
| 29 * top controls when a reader mode page has finished loading. |
| 30 * |
| 31 * TODO(aruslan): combine with ContextualSearchPanel. |
| 32 */ |
| 33 public class ReaderModePanel implements ChromeAnimation.Animatable<ReaderModePan
el.Property> { |
| 34 // TODO(aruslan): pull this from the FullscreenManager. |
| 35 private static final float TOOLBAR_HEIGHT_DP = 56.0f; |
| 36 |
| 37 private static final float PANEL_HEIGHT_DP = TOOLBAR_HEIGHT_DP; |
| 38 private static final float SHADOW_HEIGHT_DP = 4.0f; |
| 39 private static final float MINIMAL_BORDER_X_DP = 4.0f; |
| 40 private static final float DARKEN_LAYOUTTAB_BRIGHTNESS = 0.3f; |
| 41 private static final float MAX_LAYOUTTAB_DISPLACEMENT = 3.0f * TOOLBAR_HEIGH
T_DP; |
| 42 |
| 43 private static final float SNAP_BACK_THRESHOLD = 0.3f; |
| 44 private static final long BASE_ANIMATION_DURATION_MS = 500; |
| 45 |
| 46 /** |
| 47 * Panel's host interface. |
| 48 */ |
| 49 public interface ReaderModePanelHost { |
| 50 /** |
| 51 * @return Whether the reader mode button should be animated. |
| 52 */ |
| 53 boolean allowReaderModeButtonAnimation(); |
| 54 |
| 55 /** |
| 56 * @return Reader mode header background color. |
| 57 */ |
| 58 int getReaderModeHeaderBackgroundColor(); |
| 59 |
| 60 /** |
| 61 * @return One of ReaderModeManager.POSSIBLE, NOT_POSSIBLE, STARTED cons
tants. |
| 62 */ |
| 63 int getReaderModeStatus(); |
| 64 |
| 65 /** |
| 66 * @return An associated tab. |
| 67 */ |
| 68 Tab getTab(); |
| 69 |
| 70 /** |
| 71 * @param X X-coordinate in dp |
| 72 * @param Y Y-coordinate in dp |
| 73 * @return Whether a given coordinates are within the bounds of the "dis
miss" button |
| 74 */ |
| 75 public boolean isInsideDismissButton(float x, float y); |
| 76 } |
| 77 |
| 78 /** |
| 79 * Layout integration interface. |
| 80 */ |
| 81 public interface ReaderModePanelLayoutDelegate { |
| 82 /** |
| 83 * Requests a next update to refresh the transforms and changing propert
ies. |
| 84 */ |
| 85 void requestUpdate(); |
| 86 |
| 87 /** |
| 88 * Sets the brightness of the LayoutTab to a given value. |
| 89 * @param v Brightness |
| 90 */ |
| 91 void setLayoutTabBrightness(float v); |
| 92 |
| 93 /** |
| 94 * Sets the Y offset of the LayoutTab to a given value. |
| 95 * @param v Y-offset in dp |
| 96 */ |
| 97 void setLayoutTabY(float v); |
| 98 } |
| 99 |
| 100 /** |
| 101 * Properties that can be animated by using a |
| 102 * {@link org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Ani
matable}. |
| 103 */ |
| 104 public enum Property { |
| 105 /** |
| 106 * Parametric vertical slider from |
| 107 * -1.0 (panel is out of screen) to |
| 108 * 0.0 (panel is on screen) to |
| 109 * 1.0 (panel covers the entire screen) |
| 110 */ |
| 111 SLIDING_T, |
| 112 /** |
| 113 * Horizontal slider, offset in dp |
| 114 */ |
| 115 X, |
| 116 } |
| 117 |
| 118 private float mSlidingT; |
| 119 private float mX; |
| 120 |
| 121 private ScrollDirection mSwipeDirection; // set in swipeStarted |
| 122 private float mInitialPanelDistanceFromBottom; // distance from the bottom
at swipeStarted |
| 123 private float mInitialX; // X at swipeStarted |
| 124 |
| 125 /** |
| 126 * The animation set. |
| 127 */ |
| 128 private ChromeAnimation<ChromeAnimation.Animatable<?>> mLayoutAnimations; |
| 129 |
| 130 private boolean mIsReaderModePanelHidden; |
| 131 private boolean mIsReaderModePanelDismissed; |
| 132 private ContentViewCore mDistilledContentViewCore; |
| 133 private WebContentsObserver mDistilledContentObserver; |
| 134 private boolean mDidFirstNonEmptyDistilledPaint; |
| 135 private ReaderModePanelLayoutDelegate mLayoutDelegate; |
| 136 |
| 137 private float mLayoutWidth; |
| 138 private float mLayoutHeight; |
| 139 private boolean mIsToolbarShowing; |
| 140 private float mDpToPx; |
| 141 |
| 142 /** |
| 143 * The {@link ReaderModePanelHost} used to get reader mode status and the as
sociated tab. |
| 144 */ |
| 145 private final ReaderModePanelHost mReaderModeHost; |
| 146 |
| 147 /** |
| 148 * Non-animated button support. |
| 149 */ |
| 150 private boolean mAllowAnimatedButton; |
| 151 private ReaderModeButtonView mReaderModeButtonView; |
| 152 |
| 153 public ReaderModePanel(ReaderModePanelHost readerModeHost) { |
| 154 mReaderModeHost = readerModeHost; |
| 155 mAllowAnimatedButton = mReaderModeHost.allowReaderModeButtonAnimation(); |
| 156 |
| 157 mLayoutWidth = 0.0f; |
| 158 mLayoutHeight = 0.0f; |
| 159 mDpToPx = 1.0f; |
| 160 |
| 161 mSlidingT = -1.0f; |
| 162 mX = 0.0f; |
| 163 } |
| 164 |
| 165 /** |
| 166 * Destroys the panel and associated resources. |
| 167 */ |
| 168 public void onDestroy() { |
| 169 mLayoutAnimations = null; |
| 170 hideButtonBar(); |
| 171 } |
| 172 |
| 173 /** |
| 174 * Set the layout delegate. |
| 175 * @param layoutDelegate A {@link ReaderModePanelLayoutDelegate} to call. |
| 176 */ |
| 177 public void setLayoutDelegate(ReaderModePanelLayoutDelegate layoutDelegate)
{ |
| 178 mLayoutDelegate = layoutDelegate; |
| 179 requestUpdate(); |
| 180 } |
| 181 |
| 182 // ChromeAnimation.Animatable<Property>: |
| 183 |
| 184 private void setSlidingT(float val) { |
| 185 mSlidingT = val; |
| 186 if (mLayoutDelegate != null) { |
| 187 mLayoutDelegate.setLayoutTabBrightness(getTabBrightness()); |
| 188 mLayoutDelegate.setLayoutTabY(getTabYOffset()); |
| 189 } |
| 190 } |
| 191 |
| 192 @Override |
| 193 public void setProperty(Property prop, float val) { |
| 194 switch (prop) { |
| 195 case SLIDING_T: |
| 196 setSlidingT(val); |
| 197 break; |
| 198 case X: |
| 199 mX = val; |
| 200 break; |
| 201 } |
| 202 } |
| 203 |
| 204 private static float clamp(float val, float lower, float higher) { |
| 205 return val < lower ? lower : (val > higher ? higher : val); |
| 206 } |
| 207 |
| 208 private static float interp(float factor, float start, float end) { |
| 209 return start + clamp(factor, 0.0f, 1.0f) * (end - start); |
| 210 } |
| 211 |
| 212 private float getPanelDistanceFromBottom() { |
| 213 if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, 0.0f, PANEL_HEIGHT
_DP); |
| 214 return PANEL_HEIGHT_DP + interp(mSlidingT, 0.0f, getFullscreenHeight()); |
| 215 } |
| 216 |
| 217 private float getSlidingTForPanelDistanceFromBottom(float distanceFromBottom
) { |
| 218 if (distanceFromBottom >= PANEL_HEIGHT_DP) { |
| 219 return interp( |
| 220 (distanceFromBottom - PANEL_HEIGHT_DP) / getFullscreenHeight
(), |
| 221 0.0f, 1.0f); |
| 222 } |
| 223 return interp( |
| 224 (PANEL_HEIGHT_DP - distanceFromBottom) / PANEL_HEIGHT_DP, |
| 225 0.0f, -1.0f); |
| 226 } |
| 227 |
| 228 private float getDistilledContentDistanceFromBottom() { |
| 229 if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, -PANEL_HEIGHT_DP,
0.0f); |
| 230 return interp(mSlidingT, 0.0f, getFullscreenHeight()); |
| 231 } |
| 232 |
| 233 private static float snapBackSlidingT(float v) { |
| 234 // We snap asymmetrically: 30% is enough to get it opened, but 70% is ne
cessary to dismiss. |
| 235 v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= SNAP_BACK_THRESHOLD ?
v : 0.0f); |
| 236 return Math.signum(v); |
| 237 } |
| 238 |
| 239 private static float snapBackX(float v) { |
| 240 // Horizontally we snap symmetrically: more than 70% to each side to dis
miss. |
| 241 v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= 1.0f - SNAP_BACK_THRES
HOLD ? v : 0.0f); |
| 242 return Math.signum(v); |
| 243 } |
| 244 |
| 245 // Gesture handling: |
| 246 |
| 247 /** |
| 248 * @param direction Swipe direction to test |
| 249 * @return Whether the swipe in a given direction is enabled |
| 250 */ |
| 251 public boolean isSwipeEnabled(ScrollDirection direction) { |
| 252 return !isAnimating(); |
| 253 } |
| 254 |
| 255 /** |
| 256 * Called when the swipe is started. |
| 257 * @param direction Swipe direction |
| 258 * @param x X-coordinate of the starting point in dp |
| 259 * @param y Y-coordinate of the starting point in dp |
| 260 */ |
| 261 public void swipeStarted(ScrollDirection direction, float x, float y) { |
| 262 if (isAnimating()) return; |
| 263 |
| 264 mSwipeDirection = direction; |
| 265 mInitialPanelDistanceFromBottom = getPanelDistanceFromBottom(); |
| 266 mX = getX(); |
| 267 |
| 268 if (mSwipeDirection == ScrollDirection.UP) activatePreviewOfDistilledMod
e(); |
| 269 |
| 270 requestUpdate(); |
| 271 } |
| 272 |
| 273 /** |
| 274 * Called when the swipe is continued. |
| 275 * @param tx X-offset since the start of the swipe in dp |
| 276 * @param ty Y-offset since the start of the swipe in dp |
| 277 */ |
| 278 public void swipeUpdated(float x, float y, float dx, float dy, float tx, flo
at ty) { |
| 279 if (isAnimating()) return; |
| 280 |
| 281 if (mSwipeDirection == ScrollDirection.LEFT || mSwipeDirection == Scroll
Direction.RIGHT) { |
| 282 setProperty(ReaderModePanel.Property.X, clamp(mInitialX + tx, |
| 283 -mLayoutWidth + MINIMAL_BORDER_X_DP, mLayoutWidth - MINIMAL_
BORDER_X_DP)); |
| 284 } else { |
| 285 setProperty(ReaderModePanel.Property.SLIDING_T, |
| 286 getSlidingTForPanelDistanceFromBottom(mInitialPanelDistanceF
romBottom - ty)); |
| 287 } |
| 288 requestUpdate(); |
| 289 } |
| 290 |
| 291 /** |
| 292 * Called when the swipe is finished. |
| 293 */ |
| 294 public void swipeFinished() { |
| 295 if (isAnimating()) return; |
| 296 |
| 297 final float snappedX = snapBackX(mX / mLayoutWidth) * mLayoutWidth; |
| 298 final float snappedSlidingT = snapBackSlidingT(mSlidingT); |
| 299 if (snappedX <= -mLayoutWidth || snappedX >= mLayoutWidth) dismissButton
Bar(); |
| 300 if (snappedSlidingT < 0.0f) dismissButtonBar(); |
| 301 |
| 302 animateTo(snappedX, snappedSlidingT, true); |
| 303 } |
| 304 |
| 305 // Panel layout handling: |
| 306 |
| 307 /** |
| 308 * @return Whether the panel should be shown. |
| 309 */ |
| 310 public boolean isShowing() { |
| 311 return isPanelWithinScreenBounds() || isAnimating() || mDistilledContent
ViewCore != null; |
| 312 } |
| 313 |
| 314 /** |
| 315 * @return Whether the panel is within screen bounds. |
| 316 */ |
| 317 private boolean isPanelWithinScreenBounds() { |
| 318 return mSlidingT > -1.0f; |
| 319 } |
| 320 |
| 321 /** |
| 322 * @return The fullscreen height. |
| 323 */ |
| 324 private float getFullscreenHeight() { |
| 325 return mLayoutHeight + TOOLBAR_HEIGHT_DP; |
| 326 } |
| 327 |
| 328 public float getFullscreenY(float y) { |
| 329 if (mIsToolbarShowing) y += TOOLBAR_HEIGHT_DP * mDpToPx; |
| 330 return y; |
| 331 } |
| 332 |
| 333 public float getPanelY() { |
| 334 return getFullscreenHeight() - getPanelDistanceFromBottom() - SHADOW_HEI
GHT_DP; |
| 335 } |
| 336 |
| 337 public float getDistilledContentY() { |
| 338 return getFullscreenHeight() - getDistilledContentDistanceFromBottom() -
SHADOW_HEIGHT_DP; |
| 339 } |
| 340 |
| 341 public float getWidth() { |
| 342 return mLayoutWidth; |
| 343 } |
| 344 |
| 345 public float getPanelHeight() { |
| 346 return getPanelDistanceFromBottom(); |
| 347 } |
| 348 |
| 349 public float getMarginTop() { |
| 350 return SHADOW_HEIGHT_DP; |
| 351 } |
| 352 |
| 353 public float getDistilledHeight() { |
| 354 return getDistilledContentDistanceFromBottom(); |
| 355 } |
| 356 |
| 357 public float getX() { |
| 358 return mX; |
| 359 } |
| 360 |
| 361 public float getTextOpacity() { |
| 362 return interp(mSlidingT, 1.0f, 0.0f); |
| 363 } |
| 364 |
| 365 public float getTabBrightness() { |
| 366 return interp(mSlidingT, 1.0f, DARKEN_LAYOUTTAB_BRIGHTNESS); |
| 367 } |
| 368 |
| 369 public float getTabYOffset() { |
| 370 return interp(mSlidingT, 0.0f, -MAX_LAYOUTTAB_DISPLACEMENT); |
| 371 } |
| 372 |
| 373 /** |
| 374 * @param currentOffset The current top controls offset in dp. |
| 375 * @return {@link Float#NaN} if no offset should be used, or a value in dp |
| 376 * if the top controls offset should be overridden. |
| 377 */ |
| 378 public float getTopControlsOffset(float currentOffsetDp) { |
| 379 if (mSlidingT <= 0.0f) return Float.NaN; |
| 380 return MathUtils.clamp(getTabYOffset(), -TOOLBAR_HEIGHT_DP, Math.min(cur
rentOffsetDp, 0f)); |
| 381 } |
| 382 |
| 383 |
| 384 public ContentViewCore getDistilledContentViewCore() { |
| 385 return mDistilledContentViewCore; |
| 386 } |
| 387 |
| 388 public boolean didFirstNonEmptyDistilledPaint() { |
| 389 return mDidFirstNonEmptyDistilledPaint; |
| 390 } |
| 391 |
| 392 public int getReaderModeHeaderBackgroundColor() { |
| 393 return mReaderModeHost.getReaderModeHeaderBackgroundColor(); |
| 394 } |
| 395 |
| 396 /** |
| 397 * Called when the size of the view has changed. |
| 398 * |
| 399 * @param width The new width in dp. |
| 400 * @param height The new width in dp. |
| 401 * @param isToolbarShowing Whether the Toolbar is showing. |
| 402 * @param dpToPx Multipler to convert from dp to pixels. |
| 403 */ |
| 404 public void onSizeChanged(float width, float height, boolean isToolbarShowin
g, float dpToPx) { |
| 405 mLayoutWidth = width; |
| 406 mLayoutHeight = height; |
| 407 mIsToolbarShowing = isToolbarShowing; |
| 408 mDpToPx = dpToPx; |
| 409 } |
| 410 |
| 411 // Layout integration: |
| 412 |
| 413 /** |
| 414 * Requests a new frame to be updated and rendered. |
| 415 */ |
| 416 private void requestUpdate() { |
| 417 if (mLayoutDelegate != null) mLayoutDelegate.requestUpdate(); |
| 418 } |
| 419 |
| 420 // Animation handling: |
| 421 |
| 422 /** |
| 423 * @return Whether a panel animation is in progress. |
| 424 */ |
| 425 private boolean isAnimating() { |
| 426 return mLayoutAnimations != null && !mLayoutAnimations.finished(); |
| 427 } |
| 428 |
| 429 /** |
| 430 * Animates to a given target value. |
| 431 * @param targetX A target value for the X parameter |
| 432 * @param targetSlidingT A target value for the SlidingT parameter |
| 433 */ |
| 434 private void animateTo(float targetX, float targetSlidingT, boolean animate)
{ |
| 435 if (targetSlidingT > 0.0f) activatePreviewOfDistilledMode(); |
| 436 |
| 437 if (isAnimating()) { |
| 438 mLayoutAnimations.cancel(this, Property.SLIDING_T); |
| 439 mLayoutAnimations.cancel(this, Property.X); |
| 440 } |
| 441 if (mLayoutAnimations == null || mLayoutAnimations.finished()) { |
| 442 mLayoutAnimations = new ChromeAnimation<Animatable<?>>(); |
| 443 } |
| 444 |
| 445 mLayoutAnimations.add(createAnimation( |
| 446 this, Property.SLIDING_T, mSlidingT, targetSlidingT, |
| 447 BASE_ANIMATION_DURATION_MS, 0, false, |
| 448 ChromeAnimation.getDecelerateInterpolator())); |
| 449 mLayoutAnimations.add(createAnimation( |
| 450 this, Property.X, mX, targetX, |
| 451 BASE_ANIMATION_DURATION_MS, 0, false, |
| 452 ChromeAnimation.getDecelerateInterpolator())); |
| 453 mLayoutAnimations.start(); |
| 454 |
| 455 if (!animate) mLayoutAnimations.updateAndFinish(); |
| 456 requestUpdate(); |
| 457 } |
| 458 |
| 459 /** |
| 460 * Steps the animation forward and updates all the animated values. |
| 461 * @param time The current time of the app in ms. |
| 462 * @param jumpToEnd Whether to finish the animation. |
| 463 * @return Whether the animation was finished. |
| 464 */ |
| 465 public boolean onUpdateAnimation(long time, boolean jumpToEnd) { |
| 466 boolean finished = true; |
| 467 if (mLayoutAnimations != null) { |
| 468 if (jumpToEnd) { |
| 469 finished = mLayoutAnimations.finished(); |
| 470 mLayoutAnimations.updateAndFinish(); |
| 471 } else { |
| 472 finished = mLayoutAnimations.update(time); |
| 473 } |
| 474 |
| 475 if (finished || jumpToEnd) { |
| 476 mLayoutAnimations = null; |
| 477 onAnimationFinished(); |
| 478 } |
| 479 requestUpdate(); |
| 480 } |
| 481 return finished; |
| 482 } |
| 483 |
| 484 /** |
| 485 * Called when layout-specific actions are needed after the animation finish
es. |
| 486 */ |
| 487 private void onAnimationFinished() { |
| 488 if (mSlidingT >= 1.0f) enterDistilledMode(); |
| 489 updateBottomButtonBar(); |
| 490 } |
| 491 |
| 492 // Gesture handling: |
| 493 |
| 494 /** |
| 495 * @param y The y coordinate in dp. |
| 496 * @return Whether the given |y| coordinate is inside the Reader mode area. |
| 497 */ |
| 498 public boolean isYCoordinateInsideReaderModePanel(float y) { |
| 499 return y >= getPanelY() || y >= getDistilledContentY(); |
| 500 } |
| 501 |
| 502 /** |
| 503 * Handles a click in the panel area. |
| 504 * @param x X-coordinate in dp |
| 505 * @param y Y-coordinate in dp |
| 506 */ |
| 507 public void handleClick(long time, float x, float y) { |
| 508 if (mReaderModeHost.isInsideDismissButton(x * mDpToPx + mX, PANEL_HEIGHT
_DP / 2)) { |
| 509 dismissButtonBar(); |
| 510 return; |
| 511 } |
| 512 |
| 513 animateTo(mX, 1.0f, true); |
| 514 } |
| 515 |
| 516 private void nonAnimatedUpdateButtomButtonBar() { |
| 517 final int status = mReaderModeHost.getReaderModeStatus(); |
| 518 final Tab tab = mReaderModeHost.getTab(); |
| 519 |
| 520 if (mReaderModeButtonView != null |
| 521 && (status != ReaderModeManager.POSSIBLE || mIsReaderModePanelHi
dden |
| 522 || mIsReaderModePanelDismissed)) { |
| 523 mReaderModeButtonView.dismiss(true); |
| 524 mReaderModeButtonView = null; |
| 525 return; |
| 526 } |
| 527 if (mReaderModeButtonView == null |
| 528 && (status == ReaderModeManager.POSSIBLE && !mIsReaderModePanelH
idden |
| 529 && !mIsReaderModePanelDismissed)) { |
| 530 mReaderModeButtonView = ReaderModeButtonView.create(tab.getContentVi
ewCore(), |
| 531 new ReaderModeButtonViewDelegate() { |
| 532 @Override |
| 533 public void onSwipeAway() { |
| 534 dismissButtonBar(); |
| 535 } |
| 536 |
| 537 @Override |
| 538 public void onClick() { |
| 539 nonAnimatedEnterDistilledMode(); |
| 540 } |
| 541 }); |
| 542 } |
| 543 } |
| 544 |
| 545 /** |
| 546 * Updates the visibility of the reader mode button bar as required. |
| 547 */ |
| 548 public void updateBottomButtonBar() { |
| 549 if (!mAllowAnimatedButton) { |
| 550 nonAnimatedUpdateButtomButtonBar(); |
| 551 return; |
| 552 } |
| 553 |
| 554 if (isAnimating()) return; |
| 555 |
| 556 final int status = mReaderModeHost.getReaderModeStatus(); |
| 557 if (isPanelWithinScreenBounds() |
| 558 && (status != ReaderModeManager.POSSIBLE |
| 559 || mIsReaderModePanelHidden || mIsReaderModePanelDismiss
ed)) { |
| 560 animateTo(0.0f, -1.0f, true); |
| 561 destroyDistilledContentViewCore(); |
| 562 requestUpdate(); |
| 563 return; |
| 564 } |
| 565 |
| 566 if (!isPanelWithinScreenBounds() |
| 567 && (status == ReaderModeManager.POSSIBLE |
| 568 && !mIsReaderModePanelHidden && !mIsReaderModePanelDismi
ssed)) { |
| 569 animateTo(0.0f, 0.0f, true); |
| 570 requestUpdate(); |
| 571 return; |
| 572 } |
| 573 } |
| 574 |
| 575 private static ContentViewCore createDistillerContentViewCore( |
| 576 Context context, WindowAndroid windowAndroid) { |
| 577 ContentViewCore cvc = new ContentViewCore(context); |
| 578 ContentView cv = new ContentView(context, cvc); |
| 579 cvc.initialize(cv, cv, ContentViewUtil.createWebContents(false, true), w
indowAndroid); |
| 580 return cvc; |
| 581 } |
| 582 |
| 583 /** |
| 584 * Prepares the distilled mode. |
| 585 */ |
| 586 public void activatePreviewOfDistilledMode() { |
| 587 if (mDistilledContentViewCore != null) return; |
| 588 |
| 589 mDidFirstNonEmptyDistilledPaint = false; |
| 590 mDistilledContentViewCore = createDistillerContentViewCore( |
| 591 mReaderModeHost.getTab().getContentViewCore().getContext(), |
| 592 mReaderModeHost.getTab().getWindowAndroid()); |
| 593 mDistilledContentObserver = new WebContentsObserver( |
| 594 mDistilledContentViewCore.getWebContents()) { |
| 595 @Override |
| 596 public void didFirstVisuallyNonEmptyPaint() { |
| 597 super.didFirstVisuallyNonEmptyPaint(); |
| 598 mDidFirstNonEmptyDistilledPaint = true; |
| 599 } |
| 600 }; |
| 601 mReaderModeHost.getTab().attachOverlayContentViewCore( |
| 602 mDistilledContentViewCore, true, false); |
| 603 DomDistillerTabUtils.distillAndView( |
| 604 mReaderModeHost.getTab().getContentViewCore().getWebContents(), |
| 605 mDistilledContentViewCore.getWebContents()); |
| 606 mDistilledContentViewCore.onShow(); |
| 607 |
| 608 mReaderModeHost.getTab().updateTopControlsState(TopControlsState.BOTH, f
alse); |
| 609 } |
| 610 |
| 611 private void nonAnimatedEnterDistilledMode() { |
| 612 RecordUserAction.record("DomDistiller_DistilledPageOpened"); |
| 613 DomDistillerTabUtils.distillCurrentPageAndView(mReaderModeHost.getTab().
getWebContents()); |
| 614 mReaderModeHost.getTab().updateTopControlsState(TopControlsState.SHOWN,
false); |
| 615 nonAnimatedUpdateButtomButtonBar(); |
| 616 } |
| 617 |
| 618 private void enterDistilledMode() { |
| 619 RecordUserAction.record("DomDistiller_DistilledPageOpened"); |
| 620 mSlidingT = -1.0f; |
| 621 requestUpdate(); |
| 622 |
| 623 mReaderModeHost.getTab().updateTopControlsState(TopControlsState.HIDDEN,
false); |
| 624 DomDistillerTabUtils.distillCurrentPageAndView(mReaderModeHost.getTab().
getWebContents()); |
| 625 destroyDistilledContentViewCore(); |
| 626 if (mLayoutDelegate != null) { |
| 627 mLayoutDelegate.setLayoutTabBrightness(1.0f); |
| 628 mLayoutDelegate.setLayoutTabY(0.0f); |
| 629 } |
| 630 |
| 631 updateBottomButtonBar(); |
| 632 } |
| 633 |
| 634 private void destroyDistilledContentViewCore() { |
| 635 if (mDistilledContentViewCore == null) return; |
| 636 |
| 637 mDistilledContentObserver.destroy(); |
| 638 mDistilledContentObserver = null; |
| 639 mReaderModeHost.getTab().detachOverlayContentViewCore(mDistilledContentV
iewCore); |
| 640 mDistilledContentViewCore.destroy(); |
| 641 mDistilledContentViewCore = null; |
| 642 } |
| 643 |
| 644 /** |
| 645 * Hides the reader mode button bar if shown. |
| 646 */ |
| 647 public void hideButtonBar() { |
| 648 if (mIsReaderModePanelHidden) return; |
| 649 |
| 650 mIsReaderModePanelHidden = true; |
| 651 updateBottomButtonBar(); |
| 652 } |
| 653 |
| 654 /** |
| 655 * Dismisses the reader mode button bar if shown. |
| 656 */ |
| 657 public void dismissButtonBar() { |
| 658 if (mIsReaderModePanelDismissed) return; |
| 659 |
| 660 mIsReaderModePanelDismissed = true; |
| 661 updateBottomButtonBar(); |
| 662 } |
| 663 |
| 664 /** |
| 665 * Shows the reader mode button bar if necessary. |
| 666 */ |
| 667 public void unhideButtonBar() { |
| 668 mIsReaderModePanelHidden = false; |
| 669 updateBottomButtonBar(); |
| 670 } |
| 671 |
| 672 /** |
| 673 * @param tab A {@link Tab}. |
| 674 * @return The panel associated with a given Tab. |
| 675 */ |
| 676 public static ReaderModePanel getReaderModePanel(Tab tab) { |
| 677 if (!(tab instanceof ChromeTab)) return null; |
| 678 ReaderModeManager manager = ((ChromeTab) tab).getReaderModeManager(); |
| 679 if (manager == null) return null; |
| 680 return manager.getReaderModePanel(); |
| 681 } |
| 682 } |
OLD | NEW |