Index: chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f4aa8fe67fe81b2f7856d4a6377ab3af7923d07f |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java |
@@ -0,0 +1,405 @@ |
+// 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.bottombar; |
+ |
+import android.content.Context; |
+ |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.browser.ChromeActivity; |
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelAnimation; |
+import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; |
+import org.chromium.content.browser.ContentViewCore; |
+ |
+/** |
+ * Controls the Overlay Panel. |
+ */ |
+public class OverlayPanel extends ContextualSearchPanelAnimation |
+ implements OverlayPanelContentFactory { |
+ |
+ /** |
+ * State of the Overlay Panel. |
+ */ |
+ public static enum PanelState { |
+ UNDEFINED, |
+ CLOSED, |
+ PEEKED, |
+ EXPANDED, |
+ MAXIMIZED; |
+ } |
+ |
+ /** |
+ * The reason for a change in the Overlay Panel's state. |
+ * TODO(mdjones): Separate generic reasons from Contextual Search reasons. |
+ */ |
+ public static enum StateChangeReason { |
+ UNKNOWN, |
+ RESET, |
+ BACK_PRESS, |
+ TEXT_SELECT_TAP, |
+ TEXT_SELECT_LONG_PRESS, |
+ INVALID_SELECTION, |
+ CLEARED_SELECTION, |
+ BASE_PAGE_TAP, |
+ BASE_PAGE_SCROLL, |
+ SEARCH_BAR_TAP, |
+ SERP_NAVIGATION, |
+ TAB_PROMOTION, |
+ CLICK, |
+ SWIPE, |
+ FLING, |
+ OPTIN, |
+ OPTOUT, |
+ CLOSE_BUTTON; |
+ } |
+ |
+ /** |
+ * The activity this panel is in. |
+ */ |
+ protected ChromeActivity mActivity; |
+ |
+ /** |
+ * The initial height of the Overlay Panel. |
+ */ |
+ private float mInitialPanelHeight; |
+ |
+ /** |
+ * Whether a touch gesture has been detected. |
+ */ |
+ private boolean mHasDetectedTouchGesture; |
+ |
+ /** |
+ * That factory that creates OverlayPanelContents. |
+ */ |
+ private OverlayPanelContentFactory mContentFactory; |
+ |
+ /** |
+ * Container for content the panel will show. |
+ */ |
+ private OverlayPanelContent mContent; |
+ |
+ // ============================================================================================ |
+ // Constructor |
+ // ============================================================================================ |
+ |
+ /** |
+ * @param context The current Android {@link Context}. |
+ * @param updateHost The {@link LayoutUpdateHost} used to request updates in the Layout. |
+ */ |
+ public OverlayPanel(Context context, LayoutUpdateHost updateHost) { |
+ super(context, updateHost); |
+ mContentFactory = this; |
+ } |
+ |
+ /** |
+ * Destroy the native components associated with this panel's content. |
+ */ |
+ public void destroy() { |
+ destroyOverlayPanelContent(); |
+ } |
+ |
+ @Override |
+ protected void onClosed(StateChangeReason reason) { |
+ destroy(); |
+ } |
+ |
+ // ============================================================================================ |
+ // General API |
+ // ============================================================================================ |
+ |
+ /** |
+ * @return True if the panel is in the PEEKED state. |
+ */ |
+ public boolean isPeeking() { |
+ return doesPanelHeightMatchState(PanelState.PEEKED); |
+ } |
+ |
+ /** |
+ * @return Whether the Panel is in its expanded state. |
+ */ |
+ public boolean isExpanded() { |
+ return doesPanelHeightMatchState(PanelState.EXPANDED); |
+ } |
+ |
+ /** |
+ * @param url The URL that the panel should load. |
+ */ |
+ public void loadUrlInPanel(String url) { |
+ getOverlayPanelContent().loadUrl(url); |
+ } |
+ |
+ /** |
+ * @return True if a URL has been loaded in the panel's current ContentViewCore. |
+ */ |
+ public boolean isProcessingPendingNavigation() { |
+ return mContent != null && mContent.isProcessingPendingNavigation(); |
+ } |
+ |
+ /** |
+ * @param activity The ChromeActivity associated with the panel. |
+ */ |
+ public void setChromeActivity(ChromeActivity activity) { |
+ mActivity = activity; |
+ } |
+ |
+ // ============================================================================================ |
+ // Content |
+ // ============================================================================================ |
+ |
+ /** |
+ * @return True if the panel's content view is showing. |
+ */ |
+ public boolean isContentShowing() { |
+ return mContent != null && mContent.isContentShowing(); |
+ } |
+ |
+ /** |
+ * @return The ContentViewCore that this panel currently holds. |
+ */ |
+ public ContentViewCore getContentViewCore() { |
+ // Expose OverlayPanelContent method. |
+ return mContent != null ? mContent.getContentViewCore() : null; |
+ } |
+ |
+ /** |
+ * Create a new OverlayPanelContent object. This can be overridden for tests. |
+ * @return A new OverlayPanelContent object. |
+ */ |
+ public OverlayPanelContent createNewOverlayPanelContent() { |
+ return new OverlayPanelContent(new OverlayContentDelegate(), |
+ new OverlayContentProgressObserver(), mActivity); |
+ } |
+ |
+ /** |
+ * @return A new OverlayPanelContent if the instance was null or the existing one. |
+ */ |
+ protected OverlayPanelContent getOverlayPanelContent() { |
+ // Only create the content when necessary |
+ if (mContent == null) { |
+ mContent = mContentFactory.createNewOverlayPanelContent(); |
+ } |
+ return mContent; |
+ } |
+ |
+ /** |
+ * Destroy the native components of the OverlayPanelContent. |
+ */ |
+ protected void destroyOverlayPanelContent() { |
+ // It is possible that an OverlayPanelContent was never created for this panel. |
+ if (mContent != null) { |
+ mContent.destroy(); |
+ mContent = null; |
+ } |
+ } |
+ |
+ /** |
+ * Sets the top control state based on the internals of the panel. |
+ */ |
+ public void updateTopControlsState() { |
+ if (mContent == null) return; |
+ |
+ if (isFullscreenSizePanel()) { |
+ // Consider the ContentView height to be fullscreen, and inform the system that |
+ // the Toolbar is always visible (from the Compositor's perspective), even though |
+ // the Toolbar and Base Page might be offset outside the screen. This means the |
+ // renderer will consider the ContentView height to be the fullscreen height |
+ // minus the Toolbar height. |
+ // |
+ // This is necessary to fix the bugs: crbug.com/510205 and crbug.com/510206 |
+ mContent.updateTopControlsState(false, true, false); |
+ } else { |
+ mContent.updateTopControlsState(true, false, false); |
+ } |
+ } |
+ |
+ /** |
+ * Remove the last entry from history provided it is in a given time frame. |
+ * @param historyUrl The URL to remove. |
+ * @param urlTimeMs The time that the URL was visited. |
+ */ |
+ public void removeLastHistoryEntry(String historyUrl, long urlTimeMs) { |
+ if (mContent == null) return; |
+ // Expose OverlayPanelContent method. |
+ mContent.removeLastHistoryEntry(historyUrl, urlTimeMs); |
+ } |
+ |
+ /** |
+ * @return The vertical scroll position of the content. |
+ */ |
+ public float getContentVerticalScroll() { |
+ return mContent.getContentVerticalScroll(); |
+ } |
+ |
+ // ============================================================================================ |
+ // ContextualSearchPanelBase methods. |
+ // ============================================================================================ |
+ |
+ @Override |
+ protected int getControlContainerHeightResource() { |
+ // TODO(mdjones): Investigate passing this in to the constructor instead. |
+ assert mActivity != null; |
+ return mActivity.getControlContainerHeightResource(); |
+ } |
+ |
+ // ============================================================================================ |
+ // Generic Event Handling |
+ // ============================================================================================ |
+ |
+ /** |
+ * Handles the beginning of the swipe gesture. |
+ */ |
+ public void handleSwipeStart() { |
+ if (animationIsRunning()) { |
+ cancelHeightAnimation(); |
+ } |
+ |
+ mHasDetectedTouchGesture = false; |
+ mInitialPanelHeight = getHeight(); |
+ } |
+ |
+ /** |
+ * Handles the movement of the swipe gesture. |
+ * |
+ * @param ty The movement's total displacement in dps. |
+ */ |
+ public void handleSwipeMove(float ty) { |
+ if (ty > 0 && getPanelState() == PanelState.MAXIMIZED) { |
+ // Resets the Content View scroll position when swiping the Panel down |
+ // after being maximized. |
+ mContent.resetContentViewScroll(); |
+ } |
+ |
+ // Negative ty value means an upward movement so subtracting ty means expanding the panel. |
+ setClampedPanelHeight(mInitialPanelHeight - ty); |
+ requestUpdate(); |
+ } |
+ |
+ /** |
+ * Handles the end of the swipe gesture. |
+ */ |
+ public void handleSwipeEnd() { |
+ // This method will be called after handleFling() and handleClick() |
+ // methods because we also need to track down the onUpOrCancel() |
+ // action from the Layout. Therefore the animation to the nearest |
+ // PanelState should only happen when no other gesture has been |
+ // detected. |
+ if (!mHasDetectedTouchGesture) { |
+ mHasDetectedTouchGesture = true; |
+ animateToNearestState(); |
+ } |
+ } |
+ |
+ /** |
+ * Handles the fling gesture. |
+ * |
+ * @param velocity The velocity of the gesture in dps per second. |
+ */ |
+ public void handleFling(float velocity) { |
+ mHasDetectedTouchGesture = true; |
+ animateToProjectedState(velocity); |
+ } |
+ |
+ /** |
+ * Handles the click gesture. |
+ * |
+ * @param time The timestamp of the gesture. |
+ * @param x The x coordinate of the gesture. |
+ * @param y The y coordinate of the gesture. |
+ */ |
+ public void handleClick(long time, float x, float y) { |
+ mHasDetectedTouchGesture = true; |
+ if (isCoordinateInsideBasePage(x, y)) { |
+ closePanel(StateChangeReason.BASE_PAGE_TAP, true); |
+ } else if (isCoordinateInsideBar(x, y) && !onInterceptBarClick()) { |
+ handleBarClick(time, x, y); |
+ } |
+ } |
+ |
+ /** |
+ * Handles the click gesture specifically on the bar. |
+ * |
+ * @param time The timestamp of the gesture. |
+ * @param x The x coordinate of the gesture. |
+ * @param y The y coordinate of the gesture. |
+ */ |
+ protected void handleBarClick(long time, float x, float y) { |
+ if (isPeeking()) { |
+ if (supportsExpandedState()) { |
+ expandPanel(StateChangeReason.SEARCH_BAR_TAP); |
+ } else { |
+ maximizePanel(StateChangeReason.SEARCH_BAR_TAP); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Allows the click on the bar to be intercepted. |
+ * @return True if the click on the bar was intercepted by this function. |
+ */ |
+ protected boolean onInterceptBarClick() { |
+ return false; |
+ } |
+ |
+ // ============================================================================================ |
+ // Gesture Event helpers |
+ // ============================================================================================ |
+ |
+ /** |
+ * @param x The x coordinate in dp. |
+ * @param y The y coordinate in dp. |
+ * @return Whether the given coordinate is inside the bar area of the overlay. |
+ */ |
+ public boolean isCoordinateInsideBar(float x, float y) { |
+ return isCoordinateInsideOverlayPanel(x, y) |
+ && y >= getOffsetY() && y <= (getOffsetY() + getSearchBarContainerHeight()); |
+ } |
+ |
+ /** |
+ * @param x The x coordinate in dp. |
+ * @param y The y coordinate in dp. |
+ * @return Whether the given coordinate is inside the Overlay Content View area. |
+ */ |
+ public boolean isCoordinateInsideContent(float x, float y) { |
+ return isCoordinateInsideOverlayPanel(x, y) |
+ && y > getContentY(); |
+ } |
+ |
+ /** |
+ * @return The horizontal offset of the Overlay Content View in dp. |
+ */ |
+ public float getContentX() { |
+ return getOffsetX(); |
+ } |
+ |
+ /** |
+ * @return The vertical offset of the Overlay Content View in dp. |
+ */ |
+ public float getContentY() { |
+ return getOffsetY() + getSearchBarContainerHeight() + getPromoHeight(); |
+ } |
+ |
+ /** |
+ * @param x The x coordinate in dp. |
+ * @param y The y coordinate in dp. |
+ * @return Whether the given coordinate is inside the Overlay Panel area. |
+ */ |
+ private boolean isCoordinateInsideOverlayPanel(float x, float y) { |
+ return y >= getOffsetY() && y <= (getOffsetY() + getHeight()) |
+ && x >= getOffsetX() && x <= (getOffsetX() + getWidth()); |
+ } |
+ |
+ /** |
+ * @param x The x coordinate in dp. |
+ * @param y The y coordinate in dp. |
+ * @return Whether the given coordinate is inside the Base Page area. |
+ */ |
+ private boolean isCoordinateInsideBasePage(float x, float y) { |
+ return !isCoordinateInsideOverlayPanel(x, y); |
+ } |
+ |
+ @VisibleForTesting |
+ public void setOverlayPanelContentFactory(OverlayPanelContentFactory factory) { |
+ mContentFactory = factory; |
+ } |
+} |