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.compositor.bottombar.readermode; | |
6 | |
7 import android.app.Activity; | |
8 import android.content.Context; | |
9 import android.graphics.RectF; | |
10 | |
11 import org.chromium.base.ActivityState; | |
12 import org.chromium.chrome.R; | |
13 import org.chromium.chrome.browser.compositor.LayerTitleCache; | |
14 import org.chromium.chrome.browser.compositor.bottombar.OverlayContentDelegate; | |
15 import org.chromium.chrome.browser.compositor.bottombar.OverlayContentProgressOb
server; | |
16 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; | |
17 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChange
Reason; | |
18 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContent; | |
19 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContentViewD
elegate; | |
20 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager; | |
21 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.Pane
lPriority; | |
22 import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; | |
23 import org.chromium.chrome.browser.compositor.scene_layer.ReaderModeSceneLayer; | |
24 import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer; | |
25 import org.chromium.chrome.browser.dom_distiller.DomDistillerTabUtils; | |
26 import org.chromium.chrome.browser.dom_distiller.ReaderModeManagerDelegate; | |
27 import org.chromium.chrome.browser.externalnav.ExternalNavigationHandler; | |
28 import org.chromium.chrome.browser.rappor.RapporServiceBridge; | |
29 import org.chromium.components.navigation_interception.NavigationParams; | |
30 import org.chromium.content.browser.ContentViewCore; | |
31 import org.chromium.content_public.browser.WebContents; | |
32 import org.chromium.ui.resources.ResourceManager; | |
33 | |
34 /** | |
35 * The panel containing reader mode. | |
36 */ | |
37 public class ReaderModePanel extends OverlayPanel { | |
38 | |
39 /** The compositor layer used for drawing the panel. */ | |
40 private ReaderModeSceneLayer mSceneLayer; | |
41 | |
42 /** Delegate for calling functions on the ReaderModeManager. */ | |
43 private ReaderModeManagerDelegate mManagerDelegate; | |
44 | |
45 /** Delegate for passing the current ContentViewCore to the layout manager.
*/ | |
46 private OverlayPanelContentViewDelegate mContentViewDelegate; | |
47 | |
48 /** The opacity of the panel bar text. */ | |
49 private float mReaderBarTextOpacity; | |
50 | |
51 /** If the timer for counting how long a user has been reading is running. *
/ | |
52 private boolean mTimerRunning; | |
53 | |
54 /** The start time in ms of the current timer. */ | |
55 private long mStartTime; | |
56 | |
57 // =========================================================================
=================== | |
58 // Constructor | |
59 // =========================================================================
=================== | |
60 | |
61 /** | |
62 * @param context The current Android {@link Context}. | |
63 * @param updateHost The {@link LayoutUpdateHost} used to request updates in
the Layout. | |
64 * @param panelManager The {@link OverlayPanelManager} used to control panel
show/hide. | |
65 * @param contentViewDelegate Notifies the activity that a ContentViewCore h
as been created. | |
66 */ | |
67 public ReaderModePanel(Context context, LayoutUpdateHost updateHost, | |
68 OverlayPanelManager panelManager, OverlayPanelContentViewDelegate co
ntentViewDelegate) { | |
69 super(context, updateHost, panelManager); | |
70 mSceneLayer = createNewReaderModeSceneLayer(); | |
71 mContentViewDelegate = contentViewDelegate; | |
72 } | |
73 | |
74 @Override | |
75 public OverlayPanelContent createNewOverlayPanelContent() { | |
76 OverlayContentDelegate delegate = new OverlayContentDelegate() { | |
77 /** | |
78 * Track if a navigation/load is the first one for this content. | |
79 */ | |
80 private boolean mIsInitialLoad = true; | |
81 | |
82 @Override | |
83 public void onContentViewCreated(ContentViewCore contentView) { | |
84 mContentViewDelegate.setOverlayPanelContentViewCore(contentView)
; | |
85 | |
86 WebContents distilledWebContents = contentView.getWebContents(); | |
87 if (distilledWebContents == null) { | |
88 closePanel(StateChangeReason.UNKNOWN, false); | |
89 return; | |
90 } | |
91 | |
92 WebContents sourceWebContents = mManagerDelegate.getBasePageWebC
ontents(); | |
93 if (sourceWebContents == null) { | |
94 closePanel(StateChangeReason.UNKNOWN, false); | |
95 return; | |
96 } | |
97 | |
98 DomDistillerTabUtils.distillAndView(sourceWebContents, distilled
WebContents); | |
99 } | |
100 | |
101 @Override | |
102 public void onContentViewDestroyed() { | |
103 mContentViewDelegate.releaseOverlayPanelContentViewCore(); | |
104 mIsInitialLoad = true; | |
105 } | |
106 | |
107 @Override | |
108 public boolean shouldInterceptNavigation(ExternalNavigationHandler e
xternalNavHandler, | |
109 NavigationParams navigationParams) { | |
110 // The initial load will be the distilled content; don't try to
open a new tab if | |
111 // this is the case. All other navigations on distilled pages wi
ll come from link | |
112 // clicks. | |
113 if (mIsInitialLoad) { | |
114 mIsInitialLoad = false; | |
115 return true; | |
116 } | |
117 if (!navigationParams.isExternalProtocol) { | |
118 mManagerDelegate.createNewTab(navigationParams.url); | |
119 return false; | |
120 } | |
121 return true; | |
122 } | |
123 }; | |
124 | |
125 return new OverlayPanelContent(delegate, new OverlayContentProgressObser
ver(), mActivity); | |
126 } | |
127 | |
128 // =========================================================================
=================== | |
129 // Scene Overlay | |
130 // =========================================================================
=================== | |
131 | |
132 /** | |
133 * Create a new scene layer for this panel. This should be overridden by tes
ts as necessary. | |
134 */ | |
135 protected ReaderModeSceneLayer createNewReaderModeSceneLayer() { | |
136 return new ReaderModeSceneLayer(mContext.getResources().getDisplayMetric
s().density); | |
137 } | |
138 | |
139 @Override | |
140 public SceneOverlayLayer getUpdatedSceneOverlayTree(RectF viewport, RectF vi
sibleViewport, | |
141 LayerTitleCache layerTitleCache, ResourceManager resourceManager, fl
oat yOffset) { | |
142 mSceneLayer.update(resourceManager, this, getBarTextViewId(), mReaderBar
TextOpacity); | |
143 | |
144 return mSceneLayer; | |
145 } | |
146 | |
147 @Override | |
148 public boolean updateOverlay(long time, long dt) { | |
149 // This will cause the ContentViewCore to size itself appropriately for
the panel (includes | |
150 // browser controls height). | |
151 updateBrowserControlsState(); | |
152 | |
153 return super.updateOverlay(time, dt); | |
154 } | |
155 | |
156 // =========================================================================
=================== | |
157 // Manager Integration | |
158 // =========================================================================
=================== | |
159 | |
160 /** | |
161 * Sets the {@code ReaderModeManagerDelegate} associated with this panel. | |
162 * @param delegate The {@code ReaderModeManagerDelegate}. | |
163 */ | |
164 public void setManagerDelegate(ReaderModeManagerDelegate delegate) { | |
165 // TODO(mdjones): This looks similar to setManagementDelegate in Context
ualSearchPanel, | |
166 // consider moving this to OverlayPanel. | |
167 if (mManagerDelegate != delegate) { | |
168 mManagerDelegate = delegate; | |
169 if (mManagerDelegate != null) { | |
170 setChromeActivity(mManagerDelegate.getChromeActivity()); | |
171 } | |
172 } | |
173 } | |
174 | |
175 // =========================================================================
=================== | |
176 // Generic Event Handling | |
177 // =========================================================================
=================== | |
178 | |
179 @Override | |
180 public void handleBarClick(long time, float x, float y) { | |
181 super.handleBarClick(time, x, y); | |
182 if (isCoordinateInsideCloseButton(x)) { | |
183 closePanel(StateChangeReason.CLOSE_BUTTON, true); | |
184 } else { | |
185 maximizePanel(StateChangeReason.SEARCH_BAR_TAP); | |
186 } | |
187 } | |
188 | |
189 @Override | |
190 public boolean onInterceptBarClick() { | |
191 // TODO(mdjones): Handle compatibility mode here (promote to tab on tap)
. | |
192 return false; | |
193 } | |
194 | |
195 // =========================================================================
=================== | |
196 // Panel base methods | |
197 // =========================================================================
=================== | |
198 | |
199 @Override | |
200 public void destroyComponents() { | |
201 super.destroyComponents(); | |
202 destroyReaderModeBarControl(); | |
203 } | |
204 | |
205 @Override | |
206 public PanelPriority getPriority() { | |
207 return PanelPriority.MEDIUM; | |
208 } | |
209 | |
210 @Override | |
211 public boolean canBeSuppressed() { | |
212 return true; | |
213 } | |
214 | |
215 @Override | |
216 protected boolean isSupportedState(PanelState state) { | |
217 return state != PanelState.EXPANDED; | |
218 } | |
219 | |
220 @Override | |
221 protected float getThresholdToNextState() { | |
222 return 0.30f; | |
223 } | |
224 | |
225 @Override | |
226 protected void updatePanelForCloseOrPeek(float percent) { | |
227 super.updatePanelForCloseOrPeek(percent); | |
228 | |
229 // Do not update the panel text if the panel was closed immediately. | |
230 if (percent < 0.01f) return; | |
231 | |
232 getReaderModeBarControl().setBarText(R.string.reader_view_text); | |
233 mReaderBarTextOpacity = 1.0f; | |
234 } | |
235 | |
236 @Override | |
237 protected void updatePanelForMaximization(float percent) { | |
238 super.updatePanelForMaximization(percent); | |
239 if (percent < 0.5f) { | |
240 mReaderBarTextOpacity = 1.0f - 2.0f * percent; | |
241 getReaderModeBarControl().setBarText(R.string.reader_view_text); | |
242 } else { | |
243 mReaderBarTextOpacity = 2.0f * (percent - 0.5f); | |
244 getReaderModeBarControl().setBarText(R.string.reader_mode_maximized_
title); | |
245 } | |
246 } | |
247 | |
248 @Override | |
249 protected void maximizePanel(StateChangeReason reason) { | |
250 // Extend animation time by 150ms. | |
251 super.animatePanelToState(PanelState.MAXIMIZED, reason, BASE_ANIMATION_D
URATION_MS + 150); | |
252 } | |
253 | |
254 @Override | |
255 protected void onAnimationFinished() { | |
256 super.onAnimationFinished(); | |
257 boolean animatingToOpenState = getPanelState() == PanelState.MAXIMIZED; | |
258 // Start or stop the timer for how long the user has been reading. | |
259 if (!mTimerRunning && animatingToOpenState) { | |
260 mStartTime = System.currentTimeMillis(); | |
261 mTimerRunning = true; | |
262 if (mManagerDelegate != null && mManagerDelegate.getBasePageWebConte
nts() != null) { | |
263 String url = mManagerDelegate.getBasePageWebContents().getUrl(); | |
264 RapporServiceBridge.sampleDomainAndRegistryFromURL( | |
265 "DomDistiller.OpenPanel", url); | |
266 } | |
267 } else if (mTimerRunning && !animatingToOpenState) { | |
268 onTimerEnded(); | |
269 } | |
270 } | |
271 | |
272 @Override | |
273 public void peekPanel(StateChangeReason reason) { | |
274 super.peekPanel(reason); | |
275 if (mManagerDelegate == null) return; | |
276 mManagerDelegate.onPanelShown(); | |
277 } | |
278 | |
279 @Override | |
280 public void closePanel(StateChangeReason reason, boolean animate) { | |
281 super.closePanel(reason, animate); | |
282 if (mTimerRunning) { | |
283 onTimerEnded(); | |
284 } | |
285 } | |
286 | |
287 @Override | |
288 protected void onClosed(StateChangeReason reason) { | |
289 super.onClosed(reason); | |
290 if (mSceneLayer != null) mSceneLayer.hideTree(); | |
291 if (mManagerDelegate == null) return; | |
292 mManagerDelegate.onClosed(reason); | |
293 } | |
294 | |
295 /** | |
296 * Record the time spent in Reader Mode. | |
297 */ | |
298 private void onTimerEnded() { | |
299 mTimerRunning = false; | |
300 long totalTime = System.currentTimeMillis() - mStartTime; | |
301 if (mStartTime <= 0 || totalTime < 0) return; | |
302 mManagerDelegate.recordTimeSpentInReader(totalTime); | |
303 } | |
304 | |
305 @Override | |
306 public float getOffsetY() { | |
307 // Do not attempt to auto-hide the reader mode bar if the toolbar is les
s than a certain | |
308 // height. | |
309 boolean shouldAutoHide = getToolbarHeight() >= getBarHeightPeeking(); | |
310 // This will cause the reader mode bar to behave like the browser contro
ls; sliding out of | |
311 // view as the page scrolls. | |
312 return super.getOffsetY() + (shouldAutoHide ? getBrowserControlsOffsetDp
() : 0.0f); | |
313 } | |
314 | |
315 @Override | |
316 public void onLayoutChanged(float width, float height, float visibleViewport
OffsetY) { | |
317 if (width != getWidth()) destroyReaderModeBarControl(); | |
318 | |
319 super.onLayoutChanged(width, height, visibleViewportOffsetY); | |
320 } | |
321 | |
322 @Override | |
323 protected float calculateBasePageDesiredOffset() { | |
324 return -getToolbarHeight(); | |
325 } | |
326 | |
327 @Override | |
328 public void onActivityStateChange(Activity activity, int newState) { | |
329 // If the activity is only resuming, don't do anything. | |
330 if (newState == ActivityState.RESUMED) return; | |
331 super.onActivityStateChange(activity, newState); | |
332 } | |
333 | |
334 // =========================================================================
=================== | |
335 // ReaderModeBarControl | |
336 // =========================================================================
=================== | |
337 | |
338 private ReaderModeBarControl mReaderModeBarControl; | |
339 | |
340 /** | |
341 * @return The Id of the Search Term View. | |
342 */ | |
343 public int getBarTextViewId() { | |
344 return getReaderModeBarControl().getViewId(); | |
345 } | |
346 | |
347 /** | |
348 * Creates the ReaderModeBarControl, if needed. The Views are set to INVISIB
LE, because | |
349 * they won't actually be displayed on the screen (their snapshots will be d
isplayed instead). | |
350 */ | |
351 protected ReaderModeBarControl getReaderModeBarControl() { | |
352 assert mContainerView != null; | |
353 assert mResourceLoader != null; | |
354 | |
355 if (mReaderModeBarControl == null) { | |
356 mReaderModeBarControl = | |
357 new ReaderModeBarControl(this, mContext, mContainerView, mRe
sourceLoader); | |
358 } | |
359 | |
360 assert mReaderModeBarControl != null; | |
361 return mReaderModeBarControl; | |
362 } | |
363 | |
364 /** | |
365 * Destroys the ReaderModeBarControl. | |
366 */ | |
367 protected void destroyReaderModeBarControl() { | |
368 if (mReaderModeBarControl != null) { | |
369 mReaderModeBarControl.destroy(); | |
370 mReaderModeBarControl = null; | |
371 } | |
372 } | |
373 } | |
OLD | NEW |