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.layouts; |
| 6 |
| 7 import android.annotation.TargetApi; |
| 8 import android.content.Context; |
| 9 import android.graphics.RectF; |
| 10 import android.os.Build; |
| 11 import android.util.SparseArray; |
| 12 import android.view.MotionEvent; |
| 13 import android.view.ViewGroup; |
| 14 |
| 15 import org.chromium.chrome.browser.ChromeMobileApplication; |
| 16 import org.chromium.chrome.browser.Tab; |
| 17 import org.chromium.chrome.browser.UrlConstants; |
| 18 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.Context
ualSearchPanel; |
| 19 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab; |
| 20 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView; |
| 21 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; |
| 22 import org.chromium.chrome.browser.compositor.layouts.eventfilter.CascadeEventFi
lter; |
| 23 import org.chromium.chrome.browser.compositor.layouts.eventfilter.ContextualSear
chEventFilter; |
| 24 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent
Filter; |
| 25 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent
Filter.ScrollDirection; |
| 26 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandl
er; |
| 27 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EmptyEdgeSwipe
Handler; |
| 28 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter; |
| 29 import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureHandler
; |
| 30 import org.chromium.chrome.browser.compositor.layouts.phone.ContextualSearchLayo
ut; |
| 31 import org.chromium.chrome.browser.compositor.overlays.SceneOverlay; |
| 32 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDe
legate; |
| 33 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager.Cont
extualSearchContentViewDelegate; |
| 34 import org.chromium.chrome.browser.contextualsearch.ContextualSearchStaticEventF
ilter; |
| 35 import org.chromium.chrome.browser.contextualsearch.ContextualSearchStaticEventF
ilter.ContextualSearchTapHandler; |
| 36 import org.chromium.chrome.browser.device.DeviceClassManager; |
| 37 import org.chromium.chrome.browser.dom_distiller.ReaderModeEdgeSwipeHandler; |
| 38 import org.chromium.chrome.browser.dom_distiller.ReaderModePanel; |
| 39 import org.chromium.chrome.browser.dom_distiller.ReaderModeStaticEventFilter; |
| 40 import org.chromium.chrome.browser.dom_distiller.ReaderModeStaticEventFilter.Rea
derModePanelSelector; |
| 41 import org.chromium.chrome.browser.dom_distiller.ReaderModeStaticEventFilter.Rea
derModeTapHandler; |
| 42 import org.chromium.chrome.browser.fullscreen.FullscreenManager; |
| 43 import org.chromium.chrome.browser.tab.ChromeTab; |
| 44 import org.chromium.chrome.browser.tabmodel.TabCreatorManager; |
| 45 import org.chromium.chrome.browser.tabmodel.TabModel; |
| 46 import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| 47 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; |
| 48 import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
| 49 import org.chromium.chrome.browser.tabmodel.document.DocumentTabModelSelector; |
| 50 import org.chromium.chrome.browser.util.FeatureUtilities; |
| 51 import org.chromium.content.browser.ContentViewCore; |
| 52 import org.chromium.ui.resources.dynamics.DynamicResourceLoader; |
| 53 |
| 54 import java.util.List; |
| 55 |
| 56 /** |
| 57 * A {@link Layout} controller for a simple document use case. This class is re
sponsible for |
| 58 * driving all {@link Layout}s that get shown via the {@link LayoutManager}. |
| 59 */ |
| 60 public class LayoutManagerDocument extends LayoutManager |
| 61 implements ContextualSearchTapHandler, ContextualSearchContentViewDelega
te, |
| 62 ReaderModeTapHandler { |
| 63 // Layouts |
| 64 /** A {@link Layout} used for showing a normal web page. */ |
| 65 protected final StaticLayout mStaticLayout; |
| 66 /** A {@link Layout} used for when the contextual search panel is up. */ |
| 67 protected final ContextualSearchLayout mContextualSearchLayout; |
| 68 |
| 69 // Event Filters |
| 70 private final EdgeSwipeEventFilter mStaticEdgeEventFilter; |
| 71 private final ContextualSearchEventFilter mContextualSearchEventFilter; |
| 72 private final EdgeSwipeHandler mToolbarSwipeHandler; |
| 73 |
| 74 // Event Filter Handlers |
| 75 /** A {@link GestureHandler} that will delegate all events to {@link #getAct
iveLayout()}. */ |
| 76 protected final GestureHandler mGestureHandler; |
| 77 private final EdgeSwipeHandler mContextualSearchEdgeSwipeHandler; |
| 78 private final EdgeSwipeHandler mReaderModeEdgeSwipeHandler; |
| 79 |
| 80 // Internal State |
| 81 private final SparseArray<LayoutTab> mTabCache = new SparseArray<LayoutTab>(
); |
| 82 private final ContextualSearchPanel mContextualSearchPanel; |
| 83 /** A delegate for interacting with the Contextual Search manager. */ |
| 84 protected ContextualSearchManagementDelegate mContextualSearchDelegate; |
| 85 |
| 86 @SuppressWarnings("unused") private TabModelSelectorTabObserver mTabModelSel
ectorTabObserver; |
| 87 private final ReaderModePanelSelector mReaderModePanelSelector; |
| 88 |
| 89 /** |
| 90 * Creates a {@link LayoutManagerDocument} instance. |
| 91 * @param host A {@link LayoutManagerHost} instance. |
| 92 */ |
| 93 public LayoutManagerDocument(LayoutManagerHost host) { |
| 94 super(host); |
| 95 Context context = host.getContext(); |
| 96 LayoutRenderHost renderHost = host.getLayoutRenderHost(); |
| 97 |
| 98 mContextualSearchPanel = new ContextualSearchPanel(context, this); |
| 99 |
| 100 mReaderModePanelSelector = new ReaderModePanelSelector() { |
| 101 @Override |
| 102 public ReaderModePanel getActiveReaderModePanel() { |
| 103 if (mStaticLayout == null || !mStaticLayout.isActive()) return n
ull; |
| 104 return mStaticLayout.getReaderModePanel(); |
| 105 } |
| 106 }; |
| 107 |
| 108 // Build Event Filter Handlers |
| 109 mContextualSearchEdgeSwipeHandler = new ContextualSearchEdgeSwipeHandler
(this); |
| 110 mReaderModeEdgeSwipeHandler = new ReaderModeEdgeSwipeHandler( |
| 111 mReaderModePanelSelector, this); |
| 112 mGestureHandler = new GestureHandlerLayoutDelegate(this); |
| 113 mToolbarSwipeHandler = new ToolbarSwipeHandler(this); |
| 114 |
| 115 // Build Event Filters |
| 116 mStaticEdgeEventFilter = |
| 117 new EdgeSwipeEventFilter(context, this, new StaticEdgeSwipeHandl
er()); |
| 118 mContextualSearchEventFilter = new ContextualSearchEventFilter( |
| 119 context, this, mGestureHandler, mContextualSearchPanel); |
| 120 EventFilter contextualSearchStaticEventFilter = new ContextualSearchStat
icEventFilter( |
| 121 context, this, mContextualSearchPanel, mContextualSearchEdgeSwip
eHandler, this); |
| 122 EventFilter readerModeStaticEventFilter = new ReaderModeStaticEventFilte
r( |
| 123 context, this, mReaderModePanelSelector, mReaderModeEdgeSwipeHan
dler, this); |
| 124 EventFilter staticCascadeEventFilter = new CascadeEventFilter(context, t
his, |
| 125 new EventFilter[] {readerModeStaticEventFilter, contextualSearch
StaticEventFilter, |
| 126 mStaticEdgeEventFilter}); |
| 127 |
| 128 // Build Layouts |
| 129 mStaticLayout = new StaticLayout( |
| 130 context, this, renderHost, staticCascadeEventFilter, mContextual
SearchPanel); |
| 131 mContextualSearchLayout = new ContextualSearchLayout( |
| 132 context, this, renderHost, mContextualSearchEventFilter, mContex
tualSearchPanel); |
| 133 |
| 134 // Set up layout parameters |
| 135 mStaticLayout.setLayoutHandlesTabLifecycles(true); |
| 136 |
| 137 setNextLayout(null); |
| 138 } |
| 139 |
| 140 @Override |
| 141 public void init(TabModelSelector selector, TabCreatorManager creator, |
| 142 TabContentManager content, ViewGroup androidContentContainer, |
| 143 ContextualSearchManagementDelegate contextualSearchDelegate, |
| 144 DynamicResourceLoader dynamicResourceLoader) { |
| 145 // Save state |
| 146 mContextualSearchDelegate = contextualSearchDelegate; |
| 147 |
| 148 // Initialize Event Filters |
| 149 mStaticEdgeEventFilter.setTabModelSelector(selector); |
| 150 mContextualSearchEventFilter.setManagementDelegate(contextualSearchDeleg
ate); |
| 151 |
| 152 // Initialize Layouts |
| 153 mStaticLayout.setTabModelSelector(selector, content); |
| 154 mContextualSearchLayout.setTabModelSelector(selector, content); |
| 155 |
| 156 // Initialize Contextual Search Panel |
| 157 mContextualSearchPanel.setManagementDelegate(contextualSearchDelegate); |
| 158 mContextualSearchPanel.setDynamicResourceLoader(dynamicResourceLoader); |
| 159 |
| 160 // Set back flow communication |
| 161 if (contextualSearchDelegate != null) { |
| 162 contextualSearchDelegate.setContextualSearchPanelDelegate(mContextua
lSearchPanel); |
| 163 } |
| 164 |
| 165 mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(selector)
{ |
| 166 @Override |
| 167 public void onContentChanged(Tab tab) { |
| 168 initLayoutTabFromHost(tab.getId()); |
| 169 } |
| 170 |
| 171 @Override |
| 172 public void onBackgroundColorChanged(Tab tab, int color) { |
| 173 initLayoutTabFromHost(tab.getId()); |
| 174 } |
| 175 }; |
| 176 |
| 177 super.init(selector, creator, content, androidContentContainer, contextu
alSearchDelegate, |
| 178 dynamicResourceLoader); |
| 179 } |
| 180 |
| 181 @Override |
| 182 public void destroy() { |
| 183 super.destroy(); |
| 184 |
| 185 if (mStaticLayout != null) mStaticLayout.destroy(); |
| 186 if (mContextualSearchLayout != null) mContextualSearchLayout.destroy(); |
| 187 if (mTabModelSelectorTabObserver != null) mTabModelSelectorTabObserver.d
estroy(); |
| 188 } |
| 189 |
| 190 @Override |
| 191 public void getVirtualViews(List<VirtualView> views) { |
| 192 // Nothing to do here yet. |
| 193 } |
| 194 |
| 195 @Override |
| 196 protected void onViewportChanged(RectF viewportDp) { |
| 197 super.onViewportChanged(viewportDp); |
| 198 for (int i = 0; i < mTabCache.size(); i++) { |
| 199 // This assumes that the content width/height is always the size of
the host. |
| 200 mTabCache.valueAt(i).setContentSize(viewportDp.width(), viewportDp.h
eight()); |
| 201 } |
| 202 } |
| 203 |
| 204 /** |
| 205 * @return The {@link EdgeSwipeHandler} responsible for processing swipe eve
nts for the toolbar. |
| 206 */ |
| 207 @Override |
| 208 public EdgeSwipeHandler getTopSwipeHandler() { |
| 209 return mToolbarSwipeHandler; |
| 210 } |
| 211 |
| 212 /** |
| 213 * Clears all content associated with {@code tabId} from the internal caches
. |
| 214 * @param tabId The id of the tab to clear. |
| 215 */ |
| 216 protected void emptyCachesExcept(int tabId) { |
| 217 LayoutTab tab = mTabCache.get(tabId); |
| 218 mTabCache.clear(); |
| 219 if (tab != null) mTabCache.put(tabId, tab); |
| 220 } |
| 221 |
| 222 /** |
| 223 * Adds the {@link SceneOverlay} across all {@link Layout}s owned by this cl
ass. |
| 224 * @param helper A {@link SceneOverlay} instance. |
| 225 */ |
| 226 protected void addGlobalSceneOverlay(SceneOverlay helper) { |
| 227 mStaticLayout.addSceneOverlay(helper); |
| 228 mContextualSearchLayout.addSceneOverlay(helper); |
| 229 } |
| 230 |
| 231 /** |
| 232 * @param tabId The id of the tab represented by a {@link LayoutTab}. |
| 233 * @return A {@link LayoutTab} if one exists or {@code null} if none ca
n be found. |
| 234 */ |
| 235 protected LayoutTab getExistingLayoutTab(int tabId) { |
| 236 return mTabCache.get(tabId); |
| 237 } |
| 238 |
| 239 @Override |
| 240 protected Layout getDefaultLayout() { |
| 241 return mStaticLayout; |
| 242 } |
| 243 |
| 244 @Override |
| 245 public void initLayoutTabFromHost(final int tabId) { |
| 246 if (getTabModelSelector() == null || getActiveLayout() == null) return; |
| 247 |
| 248 TabModelSelector selector = getTabModelSelector(); |
| 249 ChromeTab tab = ChromeTab.fromTab(selector.getTabById(tabId)); |
| 250 if (tab == null) return; |
| 251 |
| 252 LayoutTab layoutTab = mTabCache.get(tabId); |
| 253 if (layoutTab == null) return; |
| 254 |
| 255 String url = tab.getUrl(); |
| 256 boolean isNativePage = url != null && url.startsWith(UrlConstants.CHROME
_NATIVE_SCHEME); |
| 257 boolean canUseLiveTexture = |
| 258 tab.getContentViewCore() != null && !tab.isShowingSadTab() && !i
sNativePage; |
| 259 layoutTab.initFromHost(tab.getBackgroundColor(), tab.getFallbackTextureI
d(), |
| 260 tab.shouldStall(), canUseLiveTexture); |
| 261 |
| 262 mHost.requestRender(); |
| 263 } |
| 264 |
| 265 @Override |
| 266 public LayoutTab createLayoutTab(int id, boolean incognito, boolean showClos
eButton, |
| 267 boolean isTitleNeeded, float maxContentWidth, float maxContentHeight
) { |
| 268 LayoutTab tab = mTabCache.get(id); |
| 269 if (tab == null) { |
| 270 tab = new LayoutTab(id, incognito, mLastContentWidthDp, mLastContent
HeightDp, |
| 271 showCloseButton, isTitleNeeded); |
| 272 mTabCache.put(id, tab); |
| 273 } else { |
| 274 tab.init(mLastContentWidthDp, mLastContentHeightDp, showCloseButton,
isTitleNeeded); |
| 275 } |
| 276 if (maxContentWidth > 0.f) tab.setMaxContentWidth(maxContentWidth); |
| 277 if (maxContentHeight > 0.f) tab.setMaxContentHeight(maxContentHeight); |
| 278 |
| 279 return tab; |
| 280 } |
| 281 |
| 282 @Override |
| 283 public void releaseTabLayout(int id) { |
| 284 mTabCache.remove(id); |
| 285 } |
| 286 |
| 287 @Override |
| 288 public boolean onInterceptTouchEvent(MotionEvent e, boolean isKeyboardShowin
g) { |
| 289 boolean intercepted = super.onInterceptTouchEvent(e, isKeyboardShowing); |
| 290 if (intercepted) getActiveLayout().unstallImmediately(); |
| 291 return intercepted; |
| 292 } |
| 293 |
| 294 /** |
| 295 * Should be called when the user presses the back button on the phone. |
| 296 * @return Whether or not the back button was consumed by the active {@link
Layout}. |
| 297 */ |
| 298 @Override |
| 299 public boolean onBackPressed() { |
| 300 return getActiveLayout() != null && getActiveLayout().onBackPressed(); |
| 301 } |
| 302 |
| 303 @Override |
| 304 public void handleTapContextualSearchBar(long time, float x, float y) { |
| 305 if (getActiveLayout() == mContextualSearchLayout) return; |
| 306 if (mContextualSearchDelegate == null) return; |
| 307 |
| 308 // When not in compatibility mode, tapping on the Search Bar will expand
the Panel, |
| 309 // therefore we must start showing the ContextualSearchLayout. |
| 310 // TODO(pedrosimonetti): once we implement the close button, a tap in th
e Panel might |
| 311 // trigger the Panel to close, not expand, so in that case we don't want
to show the |
| 312 // ContextualSearchLayout. Coordinate with dtrainor@ to solve this. It m
ight be |
| 313 // necessary for the ContextualSearchPanel to be able to trigger the dis
play of the |
| 314 // ContextualSearchLayout. |
| 315 if (!mContextualSearchDelegate.isRunningInCompatibilityMode()) { |
| 316 showContextualSearchLayout(true); |
| 317 } |
| 318 |
| 319 mContextualSearchPanel.handleClick(time, x, y); |
| 320 } |
| 321 |
| 322 @Override |
| 323 public void setContextualSearchContentViewCore(ContentViewCore contentViewCo
re) { |
| 324 mHost.onContentViewCoreAdded(contentViewCore); |
| 325 } |
| 326 |
| 327 @Override |
| 328 public void releaseContextualSearchContentViewCore() { |
| 329 if (getTabModelSelector() == null) return; |
| 330 Tab tab = getTabModelSelector().getCurrentTab(); |
| 331 if (tab != null) tab.updateFullscreenEnabledState(); |
| 332 } |
| 333 |
| 334 private void showContextualSearchLayout(boolean animate) { |
| 335 mContextualSearchDelegate.preserveBasePageSelectionOnNextLossOfFocus(); |
| 336 startShowing(mContextualSearchLayout, animate); |
| 337 } |
| 338 |
| 339 private class StaticEdgeSwipeHandler extends EmptyEdgeSwipeHandler { |
| 340 @Override |
| 341 public void swipeStarted(ScrollDirection direction, float x, float y) { |
| 342 } |
| 343 |
| 344 @Override |
| 345 public boolean isSwipeEnabled(ScrollDirection direction) { |
| 346 FullscreenManager fullscreenManager = mHost.getFullscreenManager(); |
| 347 return direction == ScrollDirection.DOWN && fullscreenManager != nul
l |
| 348 && fullscreenManager.getPersistentFullscreenMode(); |
| 349 } |
| 350 } |
| 351 |
| 352 private class ContextualSearchEdgeSwipeHandler extends EdgeSwipeHandlerLayou
tDelegate { |
| 353 public ContextualSearchEdgeSwipeHandler(LayoutProvider provider) { |
| 354 super(provider); |
| 355 } |
| 356 |
| 357 @Override |
| 358 public void swipeStarted(ScrollDirection direction, float x, float y) { |
| 359 if (isCompatabilityMode()) { |
| 360 mContextualSearchDelegate.openResolvedSearchUrlInNewTab(); |
| 361 return; |
| 362 } |
| 363 |
| 364 if (getActiveLayout() != mContextualSearchLayout) { |
| 365 showContextualSearchLayout(false); |
| 366 } |
| 367 |
| 368 super.swipeStarted(direction, x, y); |
| 369 } |
| 370 |
| 371 @Override |
| 372 public boolean isSwipeEnabled(ScrollDirection direction) { |
| 373 return direction == ScrollDirection.UP && mContextualSearchDelegate
!= null; |
| 374 } |
| 375 |
| 376 private boolean isCompatabilityMode() { |
| 377 return mContextualSearchDelegate != null |
| 378 && mContextualSearchDelegate.isRunningInCompatibilityMode(); |
| 379 } |
| 380 } |
| 381 |
| 382 /** |
| 383 * A {@link EdgeSwipeHandler} meant to respond to edge events for the toolba
r. |
| 384 */ |
| 385 private class ToolbarSwipeHandler extends EdgeSwipeHandlerLayoutDelegate { |
| 386 private ScrollDirection mLastScroll; |
| 387 |
| 388 /** |
| 389 * Creates an instance of the {@link ToolbarSwipeHandler}. |
| 390 * @param provider A {@link LayoutProvider} instance. |
| 391 */ |
| 392 public ToolbarSwipeHandler(LayoutProvider provider) { |
| 393 super(provider); |
| 394 } |
| 395 |
| 396 @Override |
| 397 public void swipeStarted(ScrollDirection direction, float x, float y) { |
| 398 super.swipeStarted(direction, x, y); |
| 399 mLastScroll = direction; |
| 400 } |
| 401 |
| 402 @Override |
| 403 public void swipeFinished() { |
| 404 super.swipeFinished(); |
| 405 changeTabs(); |
| 406 } |
| 407 |
| 408 @Override |
| 409 public void swipeFlingOccurred(float x, float y, float tx, float ty, flo
at vx, float vy) { |
| 410 super.swipeFlingOccurred(x, y, tx, ty, vx, vy); |
| 411 changeTabs(); |
| 412 } |
| 413 |
| 414 @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
| 415 private void changeTabs() { |
| 416 DocumentTabModelSelector selector = |
| 417 ChromeMobileApplication.getDocumentTabModelSelector(); |
| 418 TabModel tabModel = selector.getCurrentModel(); |
| 419 int currentIndex = tabModel.index(); |
| 420 if (mLastScroll == ScrollDirection.LEFT) { |
| 421 if (currentIndex < tabModel.getCount() - 1) { |
| 422 TabModelUtils.setIndex(tabModel, currentIndex + 1); |
| 423 } |
| 424 } else { |
| 425 if (currentIndex > 0) { |
| 426 TabModelUtils.setIndex(tabModel, currentIndex - 1); |
| 427 } |
| 428 } |
| 429 } |
| 430 |
| 431 @Override |
| 432 public boolean isSwipeEnabled(ScrollDirection direction) { |
| 433 FullscreenManager manager = mHost.getFullscreenManager(); |
| 434 if (getActiveLayout() != mStaticLayout |
| 435 || !DeviceClassManager.enableToolbarSwipe( |
| 436 FeatureUtilities.isDocumentMode(mHost.getContext(
))) |
| 437 || (manager != null && manager.getPersistentFullscreenMode()
)) { |
| 438 return false; |
| 439 } |
| 440 |
| 441 return direction == ScrollDirection.LEFT || direction == ScrollDirec
tion.RIGHT; |
| 442 } |
| 443 } |
| 444 |
| 445 @Override |
| 446 public void handleTapReaderModeBar(long time, float x, float y) { |
| 447 ReaderModePanel activePanel = mReaderModePanelSelector.getActiveReaderMo
dePanel(); |
| 448 if (activePanel != null) activePanel.handleClick(time, x, y); |
| 449 } |
| 450 } |
OLD | NEW |