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

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

Issue 1436743002: Integrate new Reader Mode panel (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@scene-layer-changes
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 import android.os.SystemClock;
11
12 import org.chromium.base.CommandLine;
13 import org.chromium.base.metrics.RecordHistogram;
14 import org.chromium.base.metrics.RecordUserAction;
15 import org.chromium.chrome.browser.ChromeSwitches;
16 import org.chromium.chrome.browser.WebContentsFactory;
17 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
18 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable ;
19 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent Filter.ScrollDirection;
20 import org.chromium.chrome.browser.compositor.scene_layer.ReaderModeSceneLayer;
21 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
22 import org.chromium.chrome.browser.dom_distiller.ReaderModeButtonView.ReaderMode ButtonViewDelegate;
23 import org.chromium.chrome.browser.tab.EmptyTabObserver;
24 import org.chromium.chrome.browser.tab.Tab;
25 import org.chromium.chrome.browser.util.MathUtils;
26 import org.chromium.content.browser.ContentView;
27 import org.chromium.content.browser.ContentViewClient;
28 import org.chromium.content.browser.ContentViewCore;
29 import org.chromium.content_public.browser.NavigationController;
30 import org.chromium.content_public.browser.WebContents;
31 import org.chromium.content_public.browser.WebContentsObserver;
32 import org.chromium.ui.base.WindowAndroid;
33 import org.chromium.ui.resources.ResourceManager;
34
35 import java.util.concurrent.TimeUnit;
36
37 /**
38 * Manages UI effects for reader mode including hiding and showing the
39 * reader mode and reader mode preferences toolbar icon and hiding the
40 * top controls when a reader mode page has finished loading.
41 *
42 * TODO(aruslan): combine with ContextualSearchPanel.
43 */
44 public class ReaderModePanel implements ChromeAnimation.Animatable<ReaderModePan el.Property> {
45 // TODO(aruslan): pull this from the FullscreenManager.
46 private static final float TOOLBAR_HEIGHT_DP = 56.0f;
47
48 private static final float PANEL_HEIGHT_DP = TOOLBAR_HEIGHT_DP;
49 private static final float SHADOW_HEIGHT_DP = 4.0f;
50 private static final float MINIMAL_BORDER_X_DP = 4.0f;
51 private static final float DARKEN_LAYOUTTAB_BRIGHTNESS = 0.3f;
52 private static final float MAX_LAYOUTTAB_DISPLACEMENT = 3.0f * TOOLBAR_HEIGH T_DP;
53
54 private static final float SNAP_BACK_THRESHOLD = 0.3f;
55 private static final long BASE_ANIMATION_DURATION_MS = 500;
56
57 /**
58 * Panel's host interface.
59 */
60 public interface ReaderModePanelHost {
61 /**
62 * @return Reader mode header background color.
63 */
64 int getReaderModeHeaderBackgroundColor();
65
66 /**
67 * @return One of ReaderModeManager.POSSIBLE, NOT_POSSIBLE, STARTED cons tants.
68 */
69 int getReaderModeStatus();
70
71 /**
72 * @return An associated tab.
73 */
74 Tab getTab();
75
76 /**
77 * @param X X-coordinate in dp
78 * @param Y Y-coordinate in dp
79 * @return Whether a given coordinates are within the bounds of the "dis miss" button
80 */
81 boolean isInsideDismissButton(float x, float y);
82
83 /**
84 * Creates the Reader Mode control if necessary.
85 */
86 void createReaderModeControl();
87
88 /**
89 * Destroys the Reader Mode control.
90 */
91 void destroyReaderModeControl();
92 }
93
94 /**
95 * Layout integration interface.
96 */
97 public interface ReaderModePanelLayoutDelegate {
98 /**
99 * Requests a next update to refresh the transforms and changing propert ies.
100 */
101 void requestUpdate();
102
103 /**
104 * Sets the brightness of the LayoutTab to a given value.
105 * @param v Brightness
106 */
107 void setLayoutTabBrightness(float v);
108
109 /**
110 * Sets the Y offset of the LayoutTab to a given value.
111 * @param v Y-offset in dp
112 */
113 void setLayoutTabY(float v);
114 }
115
116 /**
117 * Properties that can be animated by using a
118 * {@link org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Ani matable}.
119 */
120 public enum Property {
121 /**
122 * Parametric vertical slider from
123 * -1.0 (panel is out of screen) to
124 * 0.0 (panel is on screen) to
125 * 1.0 (panel covers the entire screen)
126 */
127 SLIDING_T,
128 /**
129 * Horizontal slider, offset in dp
130 */
131 X,
132 }
133
134 private float mSlidingT;
135 private float mX;
136
137 private ScrollDirection mSwipeDirection; // set in swipeStarted
138 private float mInitialPanelDistanceFromBottom; // distance from the bottom at swipeStarted
139 private float mInitialX; // X at swipeStarted
140
141 /**
142 * The animation set.
143 */
144 private ChromeAnimation<ChromeAnimation.Animatable<?>> mLayoutAnimations;
145
146 private boolean mIsReaderModePanelHidden;
147 private boolean mIsReaderModePanelDismissed;
148 private boolean mIsFullscreenModeEntered;
149 private boolean mIsInfobarContainerShown;
150
151 private ContentViewCore mDistilledContentViewCore;
152 private boolean mDidStartLoad;
153 private boolean mDidFinishLoad;
154 private WebContentsObserver mDistilledContentObserver;
155 private boolean mDidFirstNonEmptyDistilledPaint;
156 private ReaderModePanelLayoutDelegate mLayoutDelegate;
157 private WebContents mOriginalWebContent;
158
159 private float mLayoutWidth;
160 private float mLayoutHeight;
161 private boolean mIsToolbarShowing;
162 private float mDpToPx;
163
164 /**
165 * ContentViewClient state to override when the distilled ContentViewCore is set on the Tab.
166 */
167 private float mTopControlsOffsetYPix;
168 private float mContentOffsetYPix;
169 private float mOverdrawBottomHeightPix;
170
171 /**
172 * The {@link ReaderModePanelHost} used to get reader mode status and the as sociated tab.
173 */
174 private final ReaderModePanelHost mReaderModeHost;
175
176 /**
177 * The SceneLayer responsible for drawing the panel.
178 */
179 private ReaderModeSceneLayer mSceneLayer;
180
181 /**
182 * Non-animated button support.
183 */
184 private boolean mAllowAnimatedButton;
185 private ReaderModeButtonView mReaderModeButtonView;
186
187 public ReaderModePanel(ReaderModePanelHost readerModeHost, Context context) {
188 mReaderModeHost = readerModeHost;
189
190 // Make sure all WebContents are destroyed when a tab is closed: crbug.c om/496653
191 mReaderModeHost.getTab().addObserver(new EmptyTabObserver() {
192 @Override
193 public void onDestroyed(Tab tab) {
194 destroyCachedOriginalWebContent();
195 destroyDistilledContentViewCore();
196 }
197 });
198
199 mAllowAnimatedButton = CommandLine.getInstance().hasSwitch(
200 ChromeSwitches.ENABLE_READER_MODE_BUTTON_ANIMATION);
201
202 mLayoutWidth = 0.0f;
203 mLayoutHeight = 0.0f;
204 mDpToPx = 1.0f;
205
206 mSlidingT = -1.0f;
207 mX = 0.0f;
208
209 float dpToPx = context.getResources().getDisplayMetrics().density;
210 mSceneLayer = new ReaderModeSceneLayer(dpToPx);
211 }
212
213 /**
214 * Get this panel's SceneLayer.
215 * NOTE(mdjones): This overrides a method in OverlayPanel once the refactor is complete.
216 */
217 public SceneLayer getSceneLayer() {
218 return mSceneLayer;
219 }
220
221 /**
222 * Update this panel's SceneLayer.
223 * NOTE(mdjones): This overrides a method in OverlayPanel once the refactor is complete.
224 * @param resourceManager Resource manager for static resources.
225 */
226 public void updateSceneLayer(ResourceManager resourceManager) {
227 mSceneLayer.update(this, resourceManager);
228 }
229
230 /**
231 * Destroys the panel and associated resources.
232 */
233 public void onDestroy() {
234 hideButtonBar();
235 }
236
237 /**
238 * Set the layout delegate.
239 * @param layoutDelegate A {@link ReaderModePanelLayoutDelegate} to call.
240 */
241 public void setLayoutDelegate(ReaderModePanelLayoutDelegate layoutDelegate) {
242 mLayoutDelegate = layoutDelegate;
243 requestUpdate();
244 }
245
246 // ChromeAnimation.Animatable<Property>:
247
248 private void setSlidingT(float val) {
249 mSlidingT = val;
250 if (mLayoutDelegate != null) {
251 mLayoutDelegate.setLayoutTabBrightness(getTabBrightness());
252 mLayoutDelegate.setLayoutTabY(getTabYOffset());
253 }
254 }
255
256 @Override
257 public void setProperty(Property prop, float val) {
258 switch (prop) {
259 case SLIDING_T:
260 setSlidingT(val);
261 break;
262 case X:
263 mX = val;
264 break;
265 }
266 }
267
268 private static float clamp(float val, float lower, float higher) {
269 return val < lower ? lower : (val > higher ? higher : val);
270 }
271
272 private static float interp(float factor, float start, float end) {
273 return start + clamp(factor, 0.0f, 1.0f) * (end - start);
274 }
275
276 private float getPanelDistanceFromBottom() {
277 if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, 0.0f, PANEL_HEIGHT _DP);
278 return PANEL_HEIGHT_DP + interp(mSlidingT, 0.0f, getFullscreenHeight());
279 }
280
281 private float getSlidingTForPanelDistanceFromBottom(float distanceFromBottom ) {
282 if (distanceFromBottom >= PANEL_HEIGHT_DP) {
283 return interp(
284 (distanceFromBottom - PANEL_HEIGHT_DP) / getFullscreenHeight (),
285 0.0f, 1.0f);
286 }
287 return interp(
288 (PANEL_HEIGHT_DP - distanceFromBottom) / PANEL_HEIGHT_DP,
289 0.0f, -1.0f);
290 }
291
292 private float getDistilledContentDistanceFromBottom() {
293 if (mSlidingT < 0.0f) return interp(mSlidingT + 1.0f, -PANEL_HEIGHT_DP, 0.0f);
294 return interp(mSlidingT, 0.0f, getFullscreenHeight());
295 }
296
297 private static float snapBackSlidingT(float v) {
298 // We snap asymmetrically: 30% is enough to get it opened, but 70% is ne cessary to dismiss.
299 v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= SNAP_BACK_THRESHOLD ? v : 0.0f);
300 return Math.signum(v);
301 }
302
303 private static float snapBackX(float v) {
304 // Horizontally we snap symmetrically: more than 70% to each side to dis miss.
305 v = (v < -1.0f + SNAP_BACK_THRESHOLD) ? v : (v >= 1.0f - SNAP_BACK_THRES HOLD ? v : 0.0f);
306 return Math.signum(v);
307 }
308
309 // Gesture handling:
310
311 /**
312 * @param direction Swipe direction to test
313 * @return Whether the swipe in a given direction is enabled
314 */
315 public boolean isSwipeEnabled(ScrollDirection direction) {
316 return !isAnimating();
317 }
318
319 /**
320 * Called when the swipe is started.
321 * @param direction Swipe direction
322 * @param x X-coordinate of the starting point in dp
323 * @param y Y-coordinate of the starting point in dp
324 */
325 public void swipeStarted(ScrollDirection direction, float x, float y) {
326 if (isAnimating()) return;
327
328 mSwipeDirection = direction;
329 mInitialPanelDistanceFromBottom = getPanelDistanceFromBottom();
330 mX = getX();
331
332 if (mSwipeDirection == ScrollDirection.UP) activatePreviewOfDistilledMod e();
333
334 requestUpdate();
335 }
336
337 /**
338 * Called when the swipe is continued.
339 * @param tx X-offset since the start of the swipe in dp
340 * @param ty Y-offset since the start of the swipe in dp
341 */
342 public void swipeUpdated(float x, float y, float dx, float dy, float tx, flo at ty) {
343 if (isAnimating()) return;
344
345 if (mSwipeDirection == ScrollDirection.LEFT || mSwipeDirection == Scroll Direction.RIGHT) {
346 setProperty(ReaderModePanel.Property.X, clamp(mInitialX + tx,
347 -mLayoutWidth + MINIMAL_BORDER_X_DP, mLayoutWidth - MINIMAL_ BORDER_X_DP));
348 } else {
349 setProperty(ReaderModePanel.Property.SLIDING_T,
350 getSlidingTForPanelDistanceFromBottom(mInitialPanelDistanceF romBottom - ty));
351 }
352 requestUpdate();
353 }
354
355 /**
356 * Called when the swipe is finished.
357 */
358 public void swipeFinished() {
359 if (isAnimating()) return;
360
361 final float snappedX = snapBackX(mX / mLayoutWidth) * mLayoutWidth;
362 final float snappedSlidingT = snapBackSlidingT(mSlidingT);
363 if (snappedX <= -mLayoutWidth || snappedX >= mLayoutWidth) dismissButton Bar();
364 if (snappedSlidingT < 0.0f) dismissButtonBar();
365
366 animateTo(snappedX, snappedSlidingT, true);
367 }
368
369 // Panel layout handling:
370
371 /**
372 * @return Whether the panel should be shown.
373 */
374 public boolean isShowing() {
375 return isPanelWithinScreenBounds() || isAnimating() || mDistilledContent ViewCore != null;
376 }
377
378 /**
379 * @return Whether the panel is within screen bounds.
380 */
381 private boolean isPanelWithinScreenBounds() {
382 return mSlidingT > -1.0f;
383 }
384
385 /**
386 * @return The fullscreen height.
387 */
388 private float getFullscreenHeight() {
389 return mLayoutHeight + TOOLBAR_HEIGHT_DP;
390 }
391
392 public float getFullscreenY(float y) {
393 if (mIsToolbarShowing) y += TOOLBAR_HEIGHT_DP * mDpToPx;
394 return y;
395 }
396
397 public float getPanelY() {
398 return getFullscreenHeight() - getPanelDistanceFromBottom() - SHADOW_HEI GHT_DP;
399 }
400
401 public float getDistilledContentY() {
402 return getFullscreenHeight() - getDistilledContentDistanceFromBottom() - SHADOW_HEIGHT_DP;
403 }
404
405 public float getWidth() {
406 return mLayoutWidth;
407 }
408
409 public float getPanelHeight() {
410 return getPanelDistanceFromBottom();
411 }
412
413 public float getMarginTop() {
414 return SHADOW_HEIGHT_DP;
415 }
416
417 public float getDistilledHeight() {
418 return getDistilledContentDistanceFromBottom();
419 }
420
421 public float getX() {
422 return mX;
423 }
424
425 public float getTextOpacity() {
426 return interp(mSlidingT, 1.0f, 0.0f);
427 }
428
429 public float getTabBrightness() {
430 return interp(mSlidingT, 1.0f, DARKEN_LAYOUTTAB_BRIGHTNESS);
431 }
432
433 public float getTabYOffset() {
434 return interp(mSlidingT, 0.0f, -MAX_LAYOUTTAB_DISPLACEMENT);
435 }
436
437 /**
438 * @param currentOffset The current top controls offset in dp.
439 * @return {@link Float#NaN} if no offset should be used, or a value in dp
440 * if the top controls offset should be overridden.
441 */
442 public float getTopControlsOffset(float currentOffsetDp) {
443 if (mSlidingT <= 0.0f) return Float.NaN;
444 return MathUtils.clamp(getTabYOffset(), -TOOLBAR_HEIGHT_DP, Math.min(cur rentOffsetDp, 0f));
445 }
446
447
448 public ContentViewCore getDistilledContentViewCore() {
449 return mDistilledContentViewCore;
450 }
451
452 public boolean didFirstNonEmptyDistilledPaint() {
453 return mDidFirstNonEmptyDistilledPaint;
454 }
455
456 public int getReaderModeHeaderBackgroundColor() {
457 return mReaderModeHost.getReaderModeHeaderBackgroundColor();
458 }
459
460 /**
461 * Called when the size of the view has changed.
462 *
463 * @param width The new width in dp.
464 * @param height The new width in dp.
465 * @param isToolbarShowing Whether the Toolbar is showing.
466 * @param dpToPx Multipler to convert from dp to pixels.
467 */
468 public void onSizeChanged(float width, float height, boolean isToolbarShowin g, float dpToPx) {
469 mLayoutWidth = width;
470 mLayoutHeight = height;
471 mIsToolbarShowing = isToolbarShowing;
472 mDpToPx = dpToPx;
473 }
474
475 // Layout integration:
476
477 /**
478 * Requests a new frame to be updated and rendered.
479 */
480 private void requestUpdate() {
481 if (mLayoutDelegate != null) mLayoutDelegate.requestUpdate();
482 }
483
484 // Animation handling:
485
486 /**
487 * @return Whether a panel animation is in progress.
488 */
489 private boolean isAnimating() {
490 return mLayoutAnimations != null && !mLayoutAnimations.finished();
491 }
492
493 /**
494 * Animates to a given target value.
495 * @param targetX A target value for the X parameter
496 * @param targetSlidingT A target value for the SlidingT parameter
497 */
498 private void animateTo(float targetX, float targetSlidingT, boolean animate) {
499 if (targetSlidingT > 0.0f) activatePreviewOfDistilledMode();
500
501 if (isAnimating()) {
502 mLayoutAnimations.cancel(this, Property.SLIDING_T);
503 mLayoutAnimations.cancel(this, Property.X);
504 }
505 if (mLayoutAnimations == null || mLayoutAnimations.finished()) {
506 mLayoutAnimations = new ChromeAnimation<Animatable<?>>();
507 }
508
509 mLayoutAnimations.add(createAnimation(
510 this, Property.SLIDING_T, mSlidingT, targetSlidingT,
511 BASE_ANIMATION_DURATION_MS, 0, false,
512 ChromeAnimation.getDecelerateInterpolator()));
513 mLayoutAnimations.add(createAnimation(
514 this, Property.X, mX, targetX,
515 BASE_ANIMATION_DURATION_MS, 0, false,
516 ChromeAnimation.getDecelerateInterpolator()));
517 mLayoutAnimations.start();
518
519 if (!animate) mLayoutAnimations.updateAndFinish();
520 requestUpdate();
521 }
522
523 /**
524 * Steps the animation forward and updates all the animated values.
525 * @param time The current time of the app in ms.
526 * @param jumpToEnd Whether to finish the animation.
527 * @return Whether the animation was finished.
528 */
529 public boolean onUpdateAnimation(long time, boolean jumpToEnd) {
530 boolean finished = true;
531 if (mLayoutAnimations != null) {
532 if (jumpToEnd) {
533 finished = mLayoutAnimations.finished();
534 mLayoutAnimations.updateAndFinish();
535 } else {
536 finished = mLayoutAnimations.update(time);
537 }
538
539 if (finished || jumpToEnd) {
540 mLayoutAnimations = null;
541 onAnimationFinished();
542 }
543 requestUpdate();
544 }
545 return finished;
546 }
547
548 /**
549 * Called when layout-specific actions are needed after the animation finish es.
550 */
551 private void onAnimationFinished() {
552 if (mSlidingT >= 1.0f) enterDistilledMode();
553 updateBottomButtonBar();
554 }
555
556 // Gesture handling:
557
558 /**
559 * @param y The y coordinate in dp.
560 * @return Whether the given |y| coordinate is inside the Reader mode area.
561 */
562 public boolean isYCoordinateInsideReaderModePanel(float y) {
563 return y >= getPanelY() || y >= getDistilledContentY();
564 }
565
566 /**
567 * Handles a click in the panel area.
568 * @param x X-coordinate in dp
569 * @param y Y-coordinate in dp
570 */
571 public void handleClick(long time, float x, float y) {
572 if (mReaderModeHost.isInsideDismissButton(x * mDpToPx + mX, PANEL_HEIGHT _DP / 2)) {
573 dismissButtonBar();
574 return;
575 }
576
577 animateTo(mX, 1.0f, true);
578 }
579
580 /**
581 * @return Whether the reader mode could be currently allowed.
582 */
583 public boolean isReaderModeCurrentlyAllowed() {
584 return !mIsReaderModePanelHidden && !mIsReaderModePanelDismissed
585 && !mIsFullscreenModeEntered && !mIsInfobarContainerShown
586 && mReaderModeHost.getTab() != null
587 && mReaderModeHost.getTab().getContentViewCore() != null
588 && mReaderModeHost.getTab().getContentViewCore().getContext() != null
589 && mReaderModeHost.getTab().getWebContents() != null;
590 }
591
592 private void nonAnimatedUpdateButtomButtonBar() {
593 final int status = mReaderModeHost.getReaderModeStatus();
594 final Tab tab = mReaderModeHost.getTab();
595
596 if (mReaderModeButtonView != null
597 && (status != ReaderModeManager.POSSIBLE || !isReaderModeCurrent lyAllowed())) {
598 // Unfortunately, dismiss() couldn't be used because it might attemp t to remove a view
599 // while in onLayout, thus causing crash.
600 final ReaderModeButtonView buttonView = mReaderModeButtonView;
601 mReaderModeButtonView.post(new Runnable() {
602 @Override
603 public void run() {
604 // Unfortunately, dismiss() couldn't be used because it migh t attempt
605 // to remove a view while in onLayout, thus causing crash.
606 buttonView.removeFromParentView();
607 }
608 });
609 mReaderModeButtonView = null;
610 return;
611 }
612
613 if (mReaderModeButtonView == null
614 && (status == ReaderModeManager.POSSIBLE && isReaderModeCurrentl yAllowed())) {
615 mReaderModeButtonView = ReaderModeButtonView.create(tab.getContentVi ewCore(),
616 new ReaderModeButtonViewDelegate() {
617 @Override
618 public void onSwipeAway() {
619 dismissButtonBar();
620 }
621
622 @Override
623 public void onClick() {
624 nonAnimatedEnterDistilledMode();
625 }
626 });
627 }
628 }
629
630 /**
631 * Updates the visibility of the reader mode button bar as required.
632 */
633 public void updateBottomButtonBar() {
634 if (!mAllowAnimatedButton) {
635 nonAnimatedUpdateButtomButtonBar();
636 return;
637 }
638
639 if (isAnimating()) {
640 mReaderModeHost.createReaderModeControl();
641 return;
642 }
643
644 final int status = mReaderModeHost.getReaderModeStatus();
645 if (isPanelWithinScreenBounds()
646 && (status != ReaderModeManager.POSSIBLE || !isReaderModeCurrent lyAllowed())) {
647 animateTo(0.0f, -1.0f, true);
648 mReaderModeHost.destroyReaderModeControl();
649 destroyCachedOriginalWebContent();
650 destroyDistilledContentViewCore();
651 requestUpdate();
652 return;
653 }
654
655 if (!isPanelWithinScreenBounds()
656 && (status == ReaderModeManager.POSSIBLE && isReaderModeCurrentl yAllowed())) {
657 animateTo(0.0f, 0.0f, true);
658 mReaderModeHost.createReaderModeControl();
659 requestUpdate();
660 return;
661 }
662 }
663
664 private ContentViewCore createDistillerContentViewCore(
665 Context context, WindowAndroid windowAndroid) {
666 boolean isHostTabIncognito =
667 mReaderModeHost.getTab().getContentViewCore().getWebContents().i sIncognito();
668 ContentViewCore cvc = new ContentViewCore(context);
669 ContentView cv = ContentView.createContentView(context, cvc);
670 cvc.initialize(cv, cv, WebContentsFactory.createWebContents(isHostTabInc ognito, true),
671 windowAndroid);
672 cvc.setContentViewClient(new ContentViewClient() {
673 @Override
674 public void onOffsetsForFullscreenChanged(float topControlsOffsetYPi x,
675 float contentOffsetYPix, float overdrawBottomHeightPix) {
676 super.onOffsetsForFullscreenChanged(topControlsOffsetYPix, conte ntOffsetYPix,
677 overdrawBottomHeightPix);
678 mTopControlsOffsetYPix = topControlsOffsetYPix;
679 mContentOffsetYPix = contentOffsetYPix;
680 mOverdrawBottomHeightPix = overdrawBottomHeightPix;
681 }
682 });
683 return cvc;
684 }
685
686 /**
687 * Prepares the distilled mode.
688 */
689 public void activatePreviewOfDistilledMode() {
690 final long start = SystemClock.elapsedRealtime();
691
692 if (mDistilledContentViewCore != null) return;
693
694 mDidFirstNonEmptyDistilledPaint = false;
695 mDidStartLoad = false;
696 mDidFinishLoad = false;
697
698 destroyCachedOriginalWebContent();
699 mDistilledContentViewCore = createDistillerContentViewCore(
700 mReaderModeHost.getTab().getContentViewCore().getContext(),
701 mReaderModeHost.getTab().getWindowAndroid());
702
703 mergeNavigationHistory(mDistilledContentViewCore.getWebContents(),
704 mReaderModeHost.getTab().getWebContents());
705
706 mDistilledContentObserver = new WebContentsObserver(
707 mDistilledContentViewCore.getWebContents()) {
708 @Override
709 public void didFirstVisuallyNonEmptyPaint() {
710 super.didFirstVisuallyNonEmptyPaint();
711 mDidFirstNonEmptyDistilledPaint = true;
712
713 RecordHistogram.recordTimesHistogram("DomDistiller.Time.SwipeToP aint",
714 SystemClock.elapsedRealtime() - start, TimeUnit.MILLISEC ONDS);
715 }
716
717 @Override
718 public void didStartLoading(String url) {
719 super.didStartLoading(url);
720 mDidStartLoad = true;
721 }
722
723 @Override
724 public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
725 super.didFinishLoad(frameId, validatedUrl, isMainFrame);
726 if (isMainFrame) mDidFinishLoad = true;
727 }
728 };
729 mReaderModeHost.getTab().attachOverlayContentViewCore(
730 mDistilledContentViewCore, true, false);
731 DomDistillerTabUtils.distillAndView(
732 mReaderModeHost.getTab().getContentViewCore().getWebContents(),
733 mDistilledContentViewCore.getWebContents());
734 mDistilledContentViewCore.onShow();
735 }
736
737 private void nonAnimatedEnterDistilledMode() {
738 RecordUserAction.record("DomDistiller_DistilledPageOpened");
739 DomDistillerTabUtils.distillCurrentPageAndView(mReaderModeHost.getTab(). getWebContents());
740 nonAnimatedUpdateButtomButtonBar();
741 }
742
743 private static void mergeNavigationHistory(WebContents target, WebContents s ource) {
744 target.getNavigationController().clearHistory();
745 NavigationController distilled = target.getNavigationController();
746 NavigationController original = source.getNavigationController();
747 if (distilled.canPruneAllButLastCommitted()) {
748 distilled.copyStateFromAndPrune(original, false);
749 } else if (distilled.canCopyStateOver()) {
750 distilled.copyStateFrom(original);
751 }
752 }
753
754 private void enterDistilledMode() {
755 if (!isReaderModeCurrentlyAllowed()) return;
756
757 RecordUserAction.record("DomDistiller_DistilledPageOpened");
758 mSlidingT = -1.0f;
759 requestUpdate();
760
761 mDistilledContentViewCore.getWebContents().updateTopControlsState(true, false, false);
762
763 mReaderModeHost.getTab().detachOverlayContentViewCore(mDistilledContentV iewCore);
764 mDistilledContentObserver.destroy();
765 mDistilledContentObserver = null;
766
767 mOriginalWebContent = mReaderModeHost.getTab().getWebContents();
768
769 mDistilledContentViewCore.setContentViewClient(new ContentViewClient());
770 mReaderModeHost.getTab().swapContentViewCore(mDistilledContentViewCore, false,
771 mDidStartLoad, mDidFinishLoad);
772 mDistilledContentViewCore.getContentViewClient().onOffsetsForFullscreenC hanged(
773 mTopControlsOffsetYPix, mContentOffsetYPix, mOverdrawBottomHeigh tPix);
774
775 mDistilledContentViewCore = null;
776 destroyDistilledContentViewCore();
777
778 if (mLayoutDelegate != null) {
779 mLayoutDelegate.setLayoutTabBrightness(1.0f);
780 mLayoutDelegate.setLayoutTabY(0.0f);
781 }
782
783 updateBottomButtonBar();
784 }
785
786 private void destroyCachedOriginalWebContent() {
787 if (mOriginalWebContent != null) {
788 mOriginalWebContent.destroy();
789 mOriginalWebContent = null;
790 }
791 }
792
793 private void destroyDistilledContentViewCore() {
794 if (mDistilledContentObserver != null) {
795 mDistilledContentObserver.destroy();
796 mDistilledContentObserver = null;
797 }
798
799 if (mDistilledContentViewCore == null) return;
800
801 mReaderModeHost.getTab().detachOverlayContentViewCore(mDistilledContentV iewCore);
802
803 mDistilledContentViewCore.getWebContents().destroy();
804 mDistilledContentViewCore.destroy();
805 mDistilledContentViewCore = null;
806 }
807
808 /**
809 * Hides the reader mode button bar if shown.
810 */
811 public void hideButtonBar() {
812 mIsReaderModePanelHidden = true;
813 mLayoutAnimations = null;
814 updateBottomButtonBar();
815 }
816
817 /**
818 * Dismisses the reader mode button bar if shown.
819 */
820 public void dismissButtonBar() {
821 mIsReaderModePanelDismissed = true;
822 mLayoutAnimations = null;
823 updateBottomButtonBar();
824 }
825
826 /**
827 * Shows the reader mode button bar if necessary.
828 */
829 public void unhideButtonBar() {
830 mIsReaderModePanelHidden = false;
831 updateBottomButtonBar();
832 }
833
834 /**
835 * Temporarily hides the reader mode button while the video is shown.
836 */
837 public void onEnterFullscreen() {
838 mIsFullscreenModeEntered = true;
839 mLayoutAnimations = null;
840 updateBottomButtonBar();
841 }
842
843 /**
844 * Re-shows the reader mode button if necessary once the video is exited.
845 */
846 public void onExitFullscreen() {
847 mIsFullscreenModeEntered = false;
848 updateBottomButtonBar();
849 }
850
851 /**
852 * Temporarily hides the reader mode button while the infobars are shown.
853 */
854 public void onShowInfobarContainer() {
855 mIsInfobarContainerShown = true;
856 mLayoutAnimations = null;
857 updateBottomButtonBar();
858 }
859
860 /**
861 * Re-shows the reader mode button if necessary once the infobars are dismis sed.
862 */
863 public void onHideInfobarContainer() {
864 mIsInfobarContainerShown = false;
865 updateBottomButtonBar();
866 }
867
868 /**
869 * @param tab A {@link Tab}.
870 * @return The panel associated with a given Tab.
871 */
872 public static ReaderModePanel getReaderModePanel(Tab tab) {
873 ReaderModeManager manager = tab.getReaderModeManager();
874 if (manager == null) return null;
875 return manager.getReaderModePanel();
876 }
877 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698