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

Unified Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.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/compositor/layouts/LayoutManager.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed356d7cba6eee1dcbe5d2b1e052690645b79b48
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -0,0 +1,513 @@
+// 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.compositor.layouts;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.chromium.base.ObserverList;
+import org.chromium.base.TraceEvent;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
+import org.chromium.chrome.browser.compositor.layouts.Layout.SizingFlags;
+import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilterHost;
+import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.fullscreen.FullscreenManager;
+import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.content.browser.SPenSupport;
+import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
+
+import java.util.List;
+
+/**
+ * A class that is responsible for managing an active {@link Layout} to show to the screen. This
+ * includes lifecycle managment like showing/hiding this {@link Layout}.
+ */
+public abstract class LayoutManager implements LayoutUpdateHost, LayoutProvider, EventFilterHost {
+ /** Sampling at 60 fps. */
+ private static final long FRAME_DELTA_TIME_MS = 16;
+
+ /** Used to convert pixels to dp. */
+ protected final float mPxToDp;
+
+ /** The {@link LayoutManagerHost}, who is responsible for showing the active {@link Layout}. */
+ protected final LayoutManagerHost mHost;
+
+ /** The last X coordinate of the last {@link MotionEvent#ACTION_DOWN} event. */
+ protected int mLastTapX;
+
+ /** The last Y coordinate of the last {@link MotionEvent#ACTION_DOWN} event. */
+ protected int mLastTapY;
+
+ // External Dependencies
+ private TabModelSelector mTabModelSelector;
+ private ViewGroup mContentContainer;
+
+ // External Observers
+ private final ObserverList<SceneChangeObserver> mSceneChangeObservers;
+
+ // Current Layout State
+ private Layout mActiveLayout;
+ private Layout mNextActiveLayout;
+
+ // Current Event Fitler State
+ private EventFilter mActiveEventFilter;
+
+ // Internal State
+ private int mFullscreenToken = FullscreenManager.INVALID_TOKEN;
+ private boolean mUpdateRequested;
+
+ // Sizing State
+ private final Rect mLastViewportPx = new Rect();
+ private final Rect mLastVisibleViewportPx = new Rect();
+ private final Rect mLastFullscreenViewportPx = new Rect();
+ protected final RectF mLastViewportDp = new RectF();
+ protected final RectF mLastVisibleViewportDp = new RectF();
+ protected final RectF mLastFullscreenViewportDp = new RectF();
+
+ protected float mLastContentWidthDp;
+ protected float mLastContentHeightDp;
+ protected float mLastHeightMinusTopControlsDp;
+
+ private final RectF mCachedRectF = new RectF();
+ private final Rect mCachedRect = new Rect();
+ private final Point mCachedPoint = new Point();
+
+ /**
+ * Creates a {@link LayoutManager} instance.
+ * @param host A {@link LayoutManagerHost} instance.
+ */
+ public LayoutManager(LayoutManagerHost host) {
+ mHost = host;
+ mPxToDp = 1.f / mHost.getContext().getResources().getDisplayMetrics().density;
+ mSceneChangeObservers = new ObserverList<SceneChangeObserver>();
+
+ int hostWidth = host.getWidth();
+ int hostHeight = host.getHeight();
+ mLastViewportPx.set(0, 0, hostWidth, hostHeight);
+ mLastVisibleViewportPx.set(0, 0, hostWidth, hostHeight);
+ mLastFullscreenViewportPx.set(0, 0, hostWidth, hostHeight);
+
+ mLastContentWidthDp = hostWidth * mPxToDp;
+ mLastContentHeightDp = hostHeight * mPxToDp;
+ mLastViewportDp.set(0, 0, mLastContentWidthDp, mLastContentHeightDp);
+ mLastVisibleViewportDp.set(0, 0, mLastContentWidthDp, mLastContentHeightDp);
+ mLastFullscreenViewportDp.set(0, 0, mLastContentWidthDp, mLastContentHeightDp);
+
+ mLastHeightMinusTopControlsDp = mLastContentHeightDp;
+ }
+
+ /**
+ * @return The actual current time of the app in ms.
+ */
+ public static long time() {
+ return SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Gives the {@link LayoutManager} a chance to intercept and process touch events from the
+ * Android {@link View} system.
+ * @param e The {@link MotionEvent} that might be intercepted.
+ * @param isKeyboardShowing Whether or not the keyboard is showing.
+ * @return Whether or not this current touch gesture should be intercepted and
+ * continually forwarded to this class.
+ */
+ public boolean onInterceptTouchEvent(MotionEvent e, boolean isKeyboardShowing) {
+ if (mActiveLayout == null) return false;
+
+ if (e.getAction() == MotionEvent.ACTION_DOWN) {
+ mLastTapX = (int) e.getX();
+ mLastTapY = (int) e.getY();
+ }
+
+ Point offsets = getMotionOffsets(e);
+ mActiveEventFilter =
+ mActiveLayout.findInterceptingEventFilter(e, offsets, isKeyboardShowing);
+ if (mActiveEventFilter != null) mActiveLayout.unstallImmediately();
+ return mActiveEventFilter != null;
+ }
+
+ /**
+ * Gives the {@link LayoutManager} a chance to process the touch events from the Android
+ * {@link View} system.
+ * @param e A {@link MotionEvent} instance.
+ * @return Whether or not {@code e} was consumed.
+ */
+ public boolean onTouchEvent(MotionEvent e) {
+ if (mActiveEventFilter == null) return false;
+
+ boolean consumed = mActiveEventFilter.onTouchEvent(e);
+ Point offsets = getMotionOffsets(e);
+ if (offsets != null) mActiveEventFilter.setCurrentMotionEventOffsets(offsets.x, offsets.y);
+ return consumed;
+ }
+
+ @Override
+ public boolean propagateEvent(MotionEvent e) {
+ if (e == null) return false;
+
+ View view = getActiveLayout().getViewForInteraction();
+ if (view == null) return false;
+
+ return view.dispatchTouchEvent(e);
+ }
+
+ @Override
+ public int getViewportWidth() {
+ return mHost.getWidth();
+ }
+
+ private Point getMotionOffsets(MotionEvent e) {
+ int actionMasked = e.getActionMasked();
+ if (SPenSupport.isSPenSupported(mHost.getContext())) {
+ actionMasked = SPenSupport.convertSPenEventAction(actionMasked);
+ }
+
+ if (actionMasked == MotionEvent.ACTION_DOWN
+ || actionMasked == MotionEvent.ACTION_HOVER_ENTER) {
+ getViewportPixel(mCachedRect);
+
+ mCachedPoint.set(-mCachedRect.left, -mCachedRect.top);
+ return mCachedPoint;
+ } else if (actionMasked == MotionEvent.ACTION_UP
+ || actionMasked == MotionEvent.ACTION_CANCEL
+ || actionMasked == MotionEvent.ACTION_HOVER_EXIT) {
+ mCachedPoint.set(0, 0);
+ return mCachedPoint;
+ }
+
+ return null;
+ }
+
+ /**
+ * Updates the state of the active {@link Layout} if needed. This updates the animations and
+ * cascades the changes to the tabs.
+ */
+ public void onUpdate() {
+ TraceEvent.begin("LayoutDriver:onUpdate");
+ onUpdate(time(), FRAME_DELTA_TIME_MS);
+ TraceEvent.end("LayoutDriver:onUpdate");
+ }
+
+ /**
+ * Updates the state of the layout.
+ * @param timeMs The time in milliseconds.
+ * @param dtMs The delta time since the last update in milliseconds.
+ * @return Whether or not the {@link LayoutManager} needs more updates.
+ */
+ @VisibleForTesting
+ public boolean onUpdate(long timeMs, long dtMs) {
+ if (!mUpdateRequested) return false;
+ mUpdateRequested = false;
+ final Layout layout = getActiveLayout();
+ if (layout != null && layout.onUpdate(timeMs, dtMs) && layout.isHiding()) {
+ layout.doneHiding();
+ }
+ return mUpdateRequested;
+ }
+
+ /**
+ * Initializes the {@link LayoutManager}. Must be called before using this object.
+ * @param selector A {@link TabModelSelector} instance.
+ * @param creator A {@link TabCreatorManager} instance.
+ * @param content A {@link TabContentManager} instance.
+ * @param androidContentContainer A {@link ViewGroup} for Android views to be bound to.
+ * @param contextualSearchDelegate A {@link ContextualSearchDelegate} instance.
+ * @param dynamicResourceLoader A {@link DynamicResourceLoader} instance.
+ */
+ public void init(TabModelSelector selector, TabCreatorManager creator,
+ TabContentManager content, ViewGroup androidContentContainer,
+ ContextualSearchManagementDelegate contextualSearchDelegate,
+ DynamicResourceLoader dynamicResourceLoader) {
+ mTabModelSelector = selector;
+ mContentContainer = androidContentContainer;
+
+ if (mNextActiveLayout != null) startShowing(mNextActiveLayout, true);
+ }
+
+ /**
+ * Cleans up and destroys this object. It should not be used after this.
+ */
+ public void destroy() {
+ mSceneChangeObservers.clear();
+ }
+
+ /**
+ * @param observer Adds {@code observer} to be notified when the active {@code Layout} changes.
+ */
+ public void addSceneChangeObserver(SceneChangeObserver observer) {
+ mSceneChangeObservers.addObserver(observer);
+ }
+
+ /**
+ * @param observer Removes {@code observer}.
+ */
+ public void removeSceneChangeObserver(SceneChangeObserver observer) {
+ mSceneChangeObservers.removeObserver(observer);
+ }
+
+ /**
+ * Called when the viewport has been changed. Override this to be notified when
+ * {@link #pushNewViewport(Rect, Rect, int)} calls actually change the current viewport.
+ * @param viewportDp The new viewport in dp.
+ */
+ protected void onViewportChanged(RectF viewportDp) {
+ if (getActiveLayout() != null) {
+ getActiveLayout().sizeChanged(viewportDp, mLastVisibleViewportDp,
+ mLastFullscreenViewportDp, mLastHeightMinusTopControlsDp, getOrientation());
+ }
+ }
+
+ /**
+ * Should be called from an external source when the viewport changes. {@code viewport} and
+ * {@code visibleViewport} are different, as the top controls might be covering part of the
+ * viewport but a {@link Layout} might want to consume the whole space (or not).
+ * @param viewport The new viewport in px.
+ * @param visibleViewport The new visible viewport in px.
+ * @param heightMinusTopControls The height of the viewport minus the top controls.
+ */
+ public final void pushNewViewport(
+ Rect viewport, Rect visibleViewport, int heightMinusTopControls) {
+ mLastViewportPx.set(viewport);
+ mLastVisibleViewportPx.set(visibleViewport);
+
+ mLastViewportDp.set(viewport.left * mPxToDp, viewport.top * mPxToDp,
+ viewport.right * mPxToDp, viewport.bottom * mPxToDp);
+ mLastVisibleViewportDp.set(visibleViewport.left * mPxToDp, visibleViewport.top * mPxToDp,
+ visibleViewport.right * mPxToDp, visibleViewport.bottom * mPxToDp);
+ mLastFullscreenViewportDp.set(0, 0, viewport.right * mPxToDp, viewport.bottom * mPxToDp);
+ mLastHeightMinusTopControlsDp = heightMinusTopControls * mPxToDp;
+
+ propagateViewportToActiveLayout();
+ }
+
+ /**
+ * @return The default {@link Layout} to show when {@link Layout}s get hidden and the next
+ * {@link Layout} to show isn't known.
+ */
+ protected abstract Layout getDefaultLayout();
+
+ // TODO(dtrainor): Remove these from this control class. Split the interface?
+ @Override public abstract void initLayoutTabFromHost(final int tabId);
+
+ @Override
+ public abstract LayoutTab createLayoutTab(int id, boolean incognito, boolean showCloseButton,
+ boolean isTitleNeeded, float maxContentWidth, float maxContentHeight);
+
+ @Override public abstract void releaseTabLayout(int id);
+
+ /**
+ * @return The {@link TabModelSelector} instance this class knows about.
+ */
+ protected TabModelSelector getTabModelSelector() {
+ return mTabModelSelector;
+ }
+
+ /**
+ * @return The next {@link Layout} that will be shown. If no {@link Layout} has been set
+ * since the last time {@link #startShowing(Layout, boolean)} was called, this will be
+ * {@link #getDefaultLayout()}.
+ */
+ protected Layout getNextLayout() {
+ return mNextActiveLayout != null ? mNextActiveLayout : getDefaultLayout();
+ }
+
+ @Override
+ public Layout getActiveLayout() {
+ return mActiveLayout;
+ }
+
+ @Override
+ public RectF getViewportDp(RectF rect) {
+ if (rect == null) rect = new RectF();
+
+ if (getActiveLayout() == null) {
+ rect.set(mLastViewportDp);
+ return rect;
+ }
+
+ final int flags = getActiveLayout().getSizingFlags();
+ if ((flags & SizingFlags.REQUIRE_FULLSCREEN_SIZE) != 0) {
+ rect.set(mLastFullscreenViewportDp);
+ } else if ((flags & SizingFlags.ALLOW_TOOLBAR_HIDE) != 0) {
+ rect.set(mLastViewportDp);
+ } else {
+ rect.set(mLastVisibleViewportDp);
+ }
+
+ return rect;
+ }
+
+ @Override
+ public Rect getViewportPixel(Rect rect) {
+ if (rect == null) rect = new Rect();
+
+ if (getActiveLayout() == null) {
+ rect.set(mLastViewportPx);
+ return rect;
+ }
+
+ final int flags = getActiveLayout().getSizingFlags();
+ if ((flags & SizingFlags.REQUIRE_FULLSCREEN_SIZE) != 0) {
+ rect.set(mLastFullscreenViewportPx);
+ } else if ((flags & SizingFlags.ALLOW_TOOLBAR_HIDE) != 0) {
+ rect.set(mLastViewportPx);
+ } else {
+ rect.set(mLastVisibleViewportPx);
+ }
+ return rect;
+ }
+
+ @Override
+ public ChromeFullscreenManager getFullscreenManager() {
+ return mHost != null ? mHost.getFullscreenManager() : null;
+ }
+
+ @Override
+ public void requestUpdate() {
+ if (!mUpdateRequested) mHost.requestRender();
+ mUpdateRequested = true;
+ }
+
+ @Override
+ public void startHiding(int nextTabId, boolean hintAtTabSelection) {
+ requestUpdate();
+ if (hintAtTabSelection) {
+ for (SceneChangeObserver observer : mSceneChangeObservers) {
+ observer.onTabSelectionHinted(nextTabId);
+ }
+ }
+ }
+
+ @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE")
+ @Override
+ public void doneHiding() {
+ // TODO: If next layout is default layout clear caches (should this be a sub layout thing?)
+
+ assert mNextActiveLayout != null : "Need to have a next active layout.";
+ if (mNextActiveLayout != null) {
+ startShowing(mNextActiveLayout, true);
+ }
+ }
+
+ @Override
+ public void doneShowing() {}
+
+ /**
+ * Should be called by control logic to show a new {@link Layout}.
+ *
+ * TODO(dtrainor, clholgat): Clean up the show logic to guarantee startHiding/doneHiding get
+ * called.
+ *
+ * @param layout The new {@link Layout} to show.
+ * @param animate Whether or not {@code layout} should animate as it shows.
+ */
+ protected void startShowing(Layout layout, boolean animate) {
+ assert mTabModelSelector != null : "init() must be called first.";
+ assert layout != null : "Can't show a null layout.";
+
+ // Set the new layout
+ setNextLayout(null);
+ Layout oldLayout = getActiveLayout();
+ if (oldLayout != layout) {
+ if (oldLayout != null) {
+ oldLayout.detachViews();
+ }
+ layout.contextChanged(mHost.getContext());
+ layout.attachViews(mContentContainer);
+ mActiveLayout = layout;
+ }
+
+ ChromeFullscreenManager fullscreenManager = mHost.getFullscreenManager();
+ if (fullscreenManager != null) {
+ // Release any old fullscreen token we were holding.
+ fullscreenManager.hideControlsPersistent(mFullscreenToken);
+ mFullscreenToken = FullscreenManager.INVALID_TOKEN;
+
+ // Grab a new fullscreen token if this layout can't be in fullscreen.
+ final int flags = getActiveLayout().getSizingFlags();
+ if ((flags & SizingFlags.ALLOW_TOOLBAR_HIDE) == 0) {
+ mFullscreenToken = fullscreenManager.showControlsPersistent();
+ }
+
+ // Hide the toolbar immediately if the layout wants it gone quickly.
+ fullscreenManager.setTopControlsPermamentlyHidden(
+ flags == SizingFlags.HELPER_HIDE_TOOLBAR_IMMEDIATE);
+ }
+
+ propagateViewportToActiveLayout();
+ getActiveLayout().show(time(), animate);
+ mHost.setContentOverlayVisibility(getActiveLayout().shouldDisplayContentOverlay());
+ mHost.requestRender();
+
+ // Notify observers about the new scene.
+ for (SceneChangeObserver observer : mSceneChangeObservers) {
+ observer.onSceneChange(getActiveLayout());
+ }
+ }
+
+ /**
+ * Sets the next {@link Layout} to show after the current {@link Layout} is finished and is done
+ * hiding.
+ * @param layout The new {@link Layout} to show.
+ */
+ public void setNextLayout(Layout layout) {
+ mNextActiveLayout = (layout == null) ? getDefaultLayout() : layout;
+ }
+
+ @Override
+ public boolean isActiveLayout(Layout layout) {
+ return layout == mActiveLayout;
+ }
+
+ /**
+ * Get a list of virtual views for accessibility.
+ *
+ * @param views A List to populate with virtual views.
+ */
+ public abstract void getVirtualViews(List<VirtualView> views);
+
+ /**
+ * @return The {@link EdgeSwipeHandler} responsible for processing swipe events for the toolbar.
+ */
+ public abstract EdgeSwipeHandler getTopSwipeHandler();
+
+ /**
+ * Should be called when the user presses the back button on the phone.
+ * @return Whether or not the back button was consumed by the active {@link Layout}.
+ */
+ public abstract boolean onBackPressed();
+
+ private void propagateViewportToActiveLayout() {
+ getViewportDp(mCachedRectF);
+
+ float width = mCachedRectF.width();
+ float height = mCachedRectF.height();
+ mLastContentWidthDp = width;
+ mLastContentHeightDp = height;
+ onViewportChanged(mCachedRectF);
+ }
+
+ private int getOrientation() {
+ if (mLastContentWidthDp > mLastContentHeightDp) {
+ return Orientation.LANDSCAPE;
+ } else {
+ return Orientation.PORTRAIT;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698