Index: chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelBase.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelBase.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..91c3bd15963290015e1f15a1899d78ede66c7d96 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanelBase.java |
@@ -0,0 +1,1072 @@ |
+// 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.contextualsearch; |
+ |
+import android.content.Context; |
+ |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.PanelState; |
+import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.StateChangeReason; |
+import org.chromium.chrome.browser.util.MathUtils; |
+ |
+/** |
+ * Base abstract class for the Contextual Search Panel. |
+ */ |
+abstract class ContextualSearchPanelBase extends ContextualSearchPanelStateHandler { |
+ |
+ /** |
+ * The height of the expanded Contextual Search Panel relative to the height |
+ * of the screen. |
+ */ |
+ private static final float EXPANDED_PANEL_HEIGHT_PERCENTAGE = .7f; |
+ |
+ /** |
+ * The height of the Toolbar in dps. |
+ */ |
+ private static final float TOOLBAR_HEIGHT_DP = 56.f; |
+ |
+ /** |
+ * The height of the Contextual Search Panel's Shadow in dps. |
+ */ |
+ private static final float PANEL_SHADOW_HEIGHT_DP = 16.f; |
+ |
+ /** |
+ * The brightness of the base page when the Panel is peeking. |
+ */ |
+ private static final float BASE_PAGE_BRIGHTNESS_STATE_PEEKED = 1.f; |
+ |
+ /** |
+ * The brightness of the base page when the Panel is expanded. |
+ */ |
+ private static final float BASE_PAGE_BRIGHTNESS_STATE_EXPANDED = .7f; |
+ |
+ /** |
+ * The brightness of the base page when the Panel is maximized. |
+ */ |
+ private static final float BASE_PAGE_BRIGHTNESS_STATE_MAXIMIZED = .3f; |
+ |
+ /** |
+ * The opacity of the search provider icon when the Panel is peeking. |
+ */ |
+ private static final float SEARCH_PROVIDER_ICON_OPACITY_STATE_PEEKED = 1.f; |
+ |
+ /** |
+ * The opacity of the search provider icon when the Panel is expanded. |
+ */ |
+ private static final float SEARCH_PROVIDER_ICON_OPACITY_STATE_EXPANDED = 1.f; |
+ |
+ /** |
+ * The opacity of the search provider icon when the Panel is maximized. |
+ */ |
+ private static final float SEARCH_PROVIDER_ICON_OPACITY_STATE_MAXIMIZED = 0.f; |
+ |
+ /** |
+ * The opacity of the search icon when the Panel is peeking. |
+ */ |
+ private static final float SEARCH_ICON_OPACITY_PEEKED = 0.f; |
+ |
+ /** |
+ * The opacity of the search icon when the Panel is expanded. |
+ */ |
+ private static final float SEARCH_ICON_OPACITY_EXPANDED = 0.f; |
+ |
+ /** |
+ * The opacity of the search icon when the Panel is maximized. |
+ */ |
+ private static final float SEARCH_ICON_OPACITY_MAXIMIZED = 1.f; |
+ |
+ /** |
+ * The margin top of the Contextual Search Bar in dps. |
+ */ |
+ private static final float SEARCH_BAR_MARGIN_TOP_DP = 16.f; |
+ |
+ /** |
+ * The padding left of the Search Icon in dps. |
+ */ |
+ private static final float SEARCH_ICON_PADDING_LEFT_DP = 16.f; |
+ |
+ /** |
+ * The height of the promo state Contextual Search Panel search bar, in dps. |
+ */ |
+ private static final float SEARCH_BAR_HEIGHT_STATE_PROMO = 24.f; |
+ |
+ /** |
+ * How much the Promo Panel should displace in order to make some elements |
+ * invisible (like the Search Provider Icon and the Search Bar Text). |
+ */ |
+ private static final float DISAPPEARING_ELEMENT_THRESHOLD_DP = 10.f; |
+ |
+ /** |
+ * The height of the Search Bar's border in dps. |
+ */ |
+ private static final float SEARCH_BAR_BORDER_HEIGHT_DP = 1.f; |
+ |
+ /** |
+ * The height of the Progress Bar in dps. |
+ */ |
+ private static final float PROGRESS_BAR_HEIGHT_DP = 2.f; |
+ |
+ /** |
+ * The distance from the Progress Bar must be away from the bottom of the |
+ * screen in order to be completely visible. The closer the Progress Bar |
+ * gets to the bottom of the screen, the lower its opacity will be. When the |
+ * Progress Bar is at the very bottom of the screen (when the Search Panel |
+ * is peeking) it will be completely invisible. |
+ */ |
+ private static final float PROGRESS_BAR_VISIBILITY_THRESHOLD_DP = 10.f; |
+ |
+ /** |
+ * The height of the Search Bar when the Panel is peeking, in dps. |
+ */ |
+ private final float mSearchBarHeightPeeking; |
+ |
+ /** |
+ * The height of the Search Bar when the Panel is displaying the Promo, in dps. |
+ */ |
+ private final float mSearchBarHeightPromo; |
+ |
+ /** |
+ * The height of the Search Bar when the Panel is expanded, in dps. |
+ */ |
+ private final float mSearchBarHeightExpanded; |
+ |
+ /** |
+ * The height of the Search Bar when the Panel is maximized, in dps. |
+ */ |
+ private final float mSearchBarHeightMaximized; |
+ |
+ /** |
+ * Ratio of dps per pixel. |
+ */ |
+ private final float mPxToDp; |
+ |
+ /** |
+ * The approximate Y coordinate of the selection in pixels. |
+ */ |
+ private float mBasePageSelectionYPx = -1.f; |
+ |
+ /** |
+ * The Y coordinate to apply to the Base Page in order to keep the selection |
+ * in view when the Search Panel is in its EXPANDED state. |
+ */ |
+ private float mBasePageTargetY = 0.f; |
+ |
+ /** |
+ * The Y coordinate to apply to the Base Page in order to keep the selection |
+ * in view when the Search Panel is in its PROMO state. |
+ */ |
+ private float mPromoBasePageTargetY = 0.f; |
+ |
+ /** |
+ * Whether the Panel is showing. |
+ */ |
+ private boolean mIsShowing; |
+ |
+ // ============================================================================================ |
+ // Constructor |
+ // ============================================================================================ |
+ |
+ /** |
+ * @param context The current Android {@link Context}. |
+ */ |
+ public ContextualSearchPanelBase(Context context) { |
+ mPxToDp = 1.f / context.getResources().getDisplayMetrics().density; |
+ |
+ mSearchBarHeightPeeking = context.getResources().getDimension( |
+ R.dimen.contextual_search_bar_height) * mPxToDp; |
+ mSearchBarHeightMaximized = TOOLBAR_HEIGHT_DP + PANEL_SHADOW_HEIGHT_DP; |
+ mSearchBarHeightExpanded = |
+ Math.round((mSearchBarHeightPeeking + mSearchBarHeightMaximized) / 2.f); |
+ mSearchBarHeightPromo = SEARCH_BAR_HEIGHT_STATE_PROMO + PANEL_SHADOW_HEIGHT_DP; |
+ |
+ initializeUiState(); |
+ } |
+ |
+ // ============================================================================================ |
+ // Layout Integration |
+ // ============================================================================================ |
+ |
+ private float mLayoutWidth; |
+ private float mLayoutHeight; |
+ private boolean mIsToolbarShowing; |
+ |
+ /** |
+ * Called when the size of the view has changed. |
+ * |
+ * @param width The new width in dp. |
+ * @param height The new width in dp. |
+ * @param isToolbarShowing Whether the Toolbar is showing. |
+ */ |
+ public final void onSizeChanged(float width, float height, boolean isToolbarShowing) { |
+ mLayoutWidth = width; |
+ mLayoutHeight = height; |
+ mIsToolbarShowing = isToolbarShowing; |
+ } |
+ |
+ /** |
+ * Returns the Y position of the Contextual Search Panel. |
+ * Layouts supporting Contextual Search should override this method. |
+ * |
+ * @return The current Y-position of the Contextual Search Panel. |
+ */ |
+ public float getContextualSearchPanelY() { |
+ return getFullscreenHeight() - mHeight; |
+ } |
+ |
+ /** |
+ * @param y The y coordinate. |
+ * @return The Y coordinate relative the fullscreen height. |
+ */ |
+ public float getFullscreenY(float y) { |
+ if (mIsToolbarShowing) { |
+ y += TOOLBAR_HEIGHT_DP / mPxToDp; |
+ } |
+ return y; |
+ } |
+ |
+ /** |
+ * @return Whether the Panel is showing. |
+ */ |
+ public boolean isShowing() { |
+ return mIsShowing; |
+ } |
+ |
+ /** |
+ * Starts showing the Panel. |
+ */ |
+ protected void startShowing() { |
+ mIsShowing = true; |
+ } |
+ |
+ /** |
+ * @return The fullscreen width. |
+ */ |
+ private float getFullscreenWidth() { |
+ return mLayoutWidth; |
+ } |
+ |
+ /** |
+ * @return The fullscreen height. |
+ */ |
+ private float getFullscreenHeight() { |
+ float height = mLayoutHeight; |
+ // NOTE(pedrosimonetti): getHeight() only returns the content height |
+ // when the Toolbar is not showing. If we don't add the Toolbar height |
+ // here, there will be a "jump" when swiping the Search Panel around. |
+ // TODO(pedrosimonetti): Find better way to get the fullscreen height. |
+ if (mIsToolbarShowing) { |
+ height += TOOLBAR_HEIGHT_DP; |
+ } |
+ return height; |
+ } |
+ |
+ // ============================================================================================ |
+ // UI States |
+ // ============================================================================================ |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Test Infrastructure |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ /** |
+ * @param offsetY The vertical offset of the Contextual Search Panel to be |
+ * set. |
+ */ |
+ @VisibleForTesting |
+ public void setOffsetYForTesting(float offsetY) { |
+ mOffsetY = offsetY; |
+ } |
+ |
+ /** |
+ * @param isMaximized The setting for whether the Search Panel is fully |
+ * maximized. |
+ */ |
+ @VisibleForTesting |
+ public void setMaximizedForTesting(boolean isMaximized) { |
+ mIsMaximized = isMaximized; |
+ } |
+ |
+ /** |
+ * @param searchBarHeight The height of the Contextual Search Bar to be set. |
+ */ |
+ @VisibleForTesting |
+ public void setSearchBarHeightForTesting(float searchBarHeight) { |
+ mSearchBarHeight = searchBarHeight; |
+ } |
+ |
+ /** |
+ * @param searchBarBorderHeight The height of the Search Bar border to be |
+ * set. |
+ */ |
+ @VisibleForTesting |
+ public void setSearchBarBorderHeight(float searchBarBorderHeight) { |
+ mSearchBarBorderHeight = searchBarBorderHeight; |
+ } |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Contextual Search Panel states |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ private float mOffsetY; |
+ private float mHeight; |
+ private float mWidth; |
+ private boolean mIsMaximized; |
+ |
+ /** |
+ * @return The vertical offset of the Contextual Search Panel. |
+ */ |
+ public float getOffsetY() { |
+ return mOffsetY; |
+ } |
+ |
+ /** |
+ * @return The height of the Contextual Search Panel. |
+ */ |
+ public float getHeight() { |
+ return mHeight; |
+ } |
+ |
+ /** |
+ * @return The width of the Contextual Search Panel. |
+ */ |
+ public float getWidth() { |
+ return mWidth; |
+ } |
+ |
+ /** |
+ * @return Whether the Search Panel is fully maximized. |
+ */ |
+ public boolean isMaximized() { |
+ return mIsMaximized; |
+ } |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Contextual Search Bar states |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ private float mSearchBarMarginTop; |
+ private float mSearchBarHeight; |
+ private float mSearchBarTextOpacity; |
+ private boolean mIsSearchBarBorderVisible; |
+ private float mSearchBarBorderY; |
+ private float mSearchBarBorderHeight; |
+ private float mSearchProviderIconOpacity; |
+ private float mSearchIconPaddingLeft; |
+ private float mSearchIconOpacity; |
+ |
+ /** |
+ * @return The top margin of the Contextual Search Bar. |
+ */ |
+ public float getSearchBarMarginTop() { |
+ return mSearchBarMarginTop; |
+ } |
+ |
+ /** |
+ * @return The height of the Contextual Search Bar. |
+ */ |
+ public float getSearchBarHeight() { |
+ return mSearchBarHeight; |
+ } |
+ |
+ /** |
+ * @return The opacity of the Contextual Search Bar text. |
+ */ |
+ public float getSearchBarTextOpacity() { |
+ return mSearchBarTextOpacity; |
+ } |
+ |
+ /** |
+ * @return Whether the Search Bar border is visible. |
+ */ |
+ public boolean isSearchBarBorderVisible() { |
+ return mIsSearchBarBorderVisible; |
+ } |
+ |
+ /** |
+ * @return The Y coordinate of the Search Bar border. |
+ */ |
+ public float getSearchBarBorderY() { |
+ return mSearchBarBorderY; |
+ } |
+ |
+ /** |
+ * @return The height of the Search Bar border. |
+ */ |
+ public float getSearchBarBorderHeight() { |
+ return mSearchBarBorderHeight; |
+ } |
+ |
+ /** |
+ * @return The opacity of the search provider's icon. |
+ */ |
+ public float getSearchProviderIconOpacity() { |
+ return mSearchProviderIconOpacity; |
+ } |
+ |
+ /** |
+ * @return The left padding of the search icon. |
+ */ |
+ public float getSearchIconPaddingLeft() { |
+ return mSearchIconPaddingLeft; |
+ } |
+ |
+ /** |
+ * @return The opacity of the search icon. |
+ */ |
+ public float getSearchIconOpacity() { |
+ return mSearchIconOpacity; |
+ } |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Base Page states |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ private float mBasePageY; |
+ private float mBasePageBrightness; |
+ |
+ /** |
+ * @return The vertical offset of the base page. |
+ */ |
+ public float getBasePageY() { |
+ return mBasePageY; |
+ } |
+ |
+ /** |
+ * @return The brightness of the base page. |
+ */ |
+ public float getBasePageBrightness() { |
+ return mBasePageBrightness; |
+ } |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Progress Bar states |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ private float mProgressBarOpacity; |
+ private boolean mIsProgressBarVisible; |
+ private float mProgressBarY; |
+ private float mProgressBarHeight; |
+ private int mProgressBarCompletion; |
+ |
+ /** |
+ * @return Whether the Progress Bar is visible. |
+ */ |
+ public boolean isProgressBarVisible() { |
+ return mIsProgressBarVisible; |
+ } |
+ |
+ /** |
+ * @param isVisible Whether the Progress Bar should be visible. |
+ */ |
+ protected void setProgressBarVisible(boolean isVisible) { |
+ mIsProgressBarVisible = isVisible; |
+ } |
+ |
+ /** |
+ * @return The Y coordinate of the Progress Bar. |
+ */ |
+ public float getProgressBarY() { |
+ return mProgressBarY; |
+ } |
+ |
+ /** |
+ * @return The Progress Bar height. |
+ */ |
+ public float getProgressBarHeight() { |
+ return mProgressBarHeight; |
+ } |
+ |
+ /** |
+ * @return The Progress Bar opacity. |
+ */ |
+ public float getProgressBarOpacity() { |
+ return mProgressBarOpacity; |
+ } |
+ |
+ /** |
+ * @return The completion percentage of the Progress Bar. |
+ */ |
+ public int getProgressBarCompletion() { |
+ return mProgressBarCompletion; |
+ } |
+ |
+ /** |
+ * @param completion The completion percentage to be set. |
+ */ |
+ protected void setProgressBarCompletion(int completion) { |
+ mProgressBarCompletion = completion; |
+ } |
+ |
+ // -------------------------------------------------------------------------------------------- |
+ // Promo states |
+ // -------------------------------------------------------------------------------------------- |
+ |
+ private float mPromoContentHeight; |
+ private boolean mShouldHidePromoHeader; |
+ private static final float DEFAULT_PROMO_HEIGHT_DP = 250.f; |
+ |
+ /** |
+ * Sets the height of the promo content. |
+ */ |
+ protected void setPromoContentHeight(float height) { |
+ mPromoContentHeight = height; |
+ updateBasePageTargetY(); |
+ } |
+ |
+ /** |
+ * Gets the height of the promo content. |
+ */ |
+ public float getPromoContentHeight() { |
+ return mPromoContentHeight > 0.0f |
+ ? mPromoContentHeight : DEFAULT_PROMO_HEIGHT_DP; |
+ } |
+ |
+ /** |
+ * @param shouldHidePromoHeader Sets whether the promo's header should be hidden. |
+ */ |
+ protected void setShouldHidePromoHeader(boolean shouldHidePromoHeader) { |
+ mShouldHidePromoHeader = shouldHidePromoHeader; |
+ } |
+ |
+ /** |
+ * @return Gets whether the promo's header should be hidden. |
+ */ |
+ public boolean shouldHidePromoHeader() { |
+ return mShouldHidePromoHeader; |
+ } |
+ |
+ // ============================================================================================ |
+ // Helpers |
+ // ============================================================================================ |
+ |
+ /** |
+ * Initializes the UI state. |
+ */ |
+ private void initializeUiState() { |
+ mIsShowing = false; |
+ |
+ // Static values. |
+ mSearchBarMarginTop = SEARCH_BAR_MARGIN_TOP_DP; |
+ mSearchIconPaddingLeft = SEARCH_ICON_PADDING_LEFT_DP; |
+ mProgressBarHeight = PROGRESS_BAR_HEIGHT_DP; |
+ mSearchBarBorderHeight = SEARCH_BAR_BORDER_HEIGHT_DP; |
+ |
+ // Dynamic values. |
+ mSearchBarHeight = mSearchBarHeightPeeking; |
+ } |
+ |
+ /** |
+ * Gets the height of the Contextual Search Panel in dps for a given |
+ * |state|. |
+ * |
+ * @param state The state whose height will be calculated. |
+ * @return The height of the Contextual Search Panel in dps for a given |
+ * |state|. |
+ */ |
+ protected float getPanelHeightFromState(PanelState state) { |
+ float fullscreenHeight = getFullscreenHeight(); |
+ float panelHeight = 0; |
+ |
+ if (state == PanelState.UNDEFINED) { |
+ panelHeight = 0; |
+ } else if (state == PanelState.CLOSED) { |
+ panelHeight = 0; |
+ } else if (state == PanelState.PEEKED) { |
+ panelHeight = mSearchBarHeightPeeking; |
+ } else if (state == PanelState.PROMO) { |
+ panelHeight = getPromoContentHeight() |
+ + mSearchBarHeightPromo; |
+ } else if (state == PanelState.EXPANDED) { |
+ panelHeight = fullscreenHeight * EXPANDED_PANEL_HEIGHT_PERCENTAGE |
+ + PANEL_SHADOW_HEIGHT_DP; |
+ } else if (state == PanelState.MAXIMIZED) { |
+ panelHeight = fullscreenHeight + PANEL_SHADOW_HEIGHT_DP; |
+ } |
+ |
+ return panelHeight; |
+ } |
+ |
+ /** |
+ * Finds the state which has the nearest height compared to a given |
+ * |desiredPanelHeight|. |
+ * |
+ * @param desiredPanelHeight The height to compare to. |
+ * @return The nearest panel state. |
+ */ |
+ protected PanelState findNearestPanelStateFromHeight(float desiredPanelHeight) { |
+ PanelState closestPanelState = PanelState.CLOSED; |
+ float smallestHeightDiff = Float.POSITIVE_INFINITY; |
+ |
+ // Iterate over all states and find the one which has the nearest |
+ // height. |
+ for (PanelState state : PanelState.values()) { |
+ if (!isValidState(state)) { |
+ continue; |
+ } |
+ |
+ float height = getPanelHeightFromState(state); |
+ float heightDiff = Math.abs(desiredPanelHeight - height); |
+ if (heightDiff < smallestHeightDiff) { |
+ closestPanelState = state; |
+ smallestHeightDiff = heightDiff; |
+ } |
+ } |
+ |
+ return closestPanelState; |
+ } |
+ |
+ /** |
+ * Sets the last panel height within the limits allowable by our UI. |
+ * |
+ * @param height The height of the panel in dps. |
+ */ |
+ protected void setClampedPanelHeight(float height) { |
+ final float clampedHeight = MathUtils.clamp(height, |
+ getPanelHeightFromState(getMaximumState()), |
+ getPanelHeightFromState(PanelState.PEEKED)); |
+ setPanelHeight(clampedHeight); |
+ } |
+ |
+ @Override |
+ protected void setPanelState(PanelState state, StateChangeReason reason) { |
+ super.setPanelState(state, reason); |
+ |
+ if (state == PanelState.CLOSED) { |
+ mIsShowing = false; |
+ } |
+ } |
+ |
+ /** |
+ * Sets the panel height. |
+ * |
+ * @param height The height of the panel in dps. |
+ */ |
+ protected void setPanelHeight(float height) { |
+ updatePanelForHeight(height); |
+ } |
+ |
+ /** |
+ * @param state The Panel state. |
+ * @return Whether the Panel height matches the one from the given state. |
+ */ |
+ protected boolean doesPanelHeightMatchState(PanelState state) { |
+ return state == getPanelState() && getHeight() == getPanelHeightFromState(state); |
+ } |
+ |
+ // ============================================================================================ |
+ // UI Update Handling |
+ // ============================================================================================ |
+ |
+ /** |
+ * Updates the UI state for a given |height|. |
+ * |
+ * @param height The Contextual Search Panel height. |
+ */ |
+ private void updatePanelForHeight(float height) { |
+ PanelState endState = findLargestPanelStateFromHeight(height); |
+ PanelState startState = getPreviousPanelState(endState); |
+ float percentage = getStateCompletion(height, startState, endState); |
+ |
+ updatePanelSize(height, endState, percentage); |
+ |
+ if (endState == PanelState.CLOSED || endState == PanelState.PEEKED) { |
+ updatePanelForCloseOrPeek(percentage); |
+ } else if (endState == PanelState.EXPANDED) { |
+ updatePanelForExpansion(percentage); |
+ } else if (endState == PanelState.MAXIMIZED) { |
+ updatePanelForMaximization(percentage); |
+ } else if (endState == PanelState.PROMO) { |
+ updatePanelForOpeningPromo(percentage); |
+ } |
+ } |
+ |
+ /** |
+ * Updates the Panel size information. |
+ * |
+ * @param height The Contextual Search Panel height. |
+ * @param endState The final state of transition being executed. |
+ * @param percentage The completion percentage of the transition. |
+ */ |
+ private void updatePanelSize(float height, PanelState endState, float percentage) { |
+ mWidth = getFullscreenWidth(); |
+ mHeight = height; |
+ mOffsetY = getContextualSearchPanelY(); |
+ mIsMaximized = height == getPanelHeightFromState(PanelState.MAXIMIZED); |
+ } |
+ |
+ /** |
+ * Finds the largest Panel state which is being transitioned to/from. |
+ * Whenever the Panel is in between states, let's say, when resizing the |
+ * Panel from its peeked to expanded state, we need to know those two states |
+ * in order to calculate how closely we are from one of them. This method |
+ * will always return the nearest state with the largest height, and |
+ * together with the state preceding it, it's possible to calculate how far |
+ * the Panel is from them. |
+ * |
+ * @param panelHeight The height to compare to. |
+ * @return The panel state which is being transitioned to/from. |
+ */ |
+ private PanelState findLargestPanelStateFromHeight(float panelHeight) { |
+ PanelState stateFound = PanelState.CLOSED; |
+ |
+ // Iterate over all states and find the largest one which is being |
+ // transitioned to/from. |
+ for (PanelState state : PanelState.values()) { |
+ if (!isValidState(state)) { |
+ continue; |
+ } |
+ if (panelHeight <= getPanelHeightFromState(state)) { |
+ stateFound = state; |
+ break; |
+ } |
+ } |
+ |
+ return stateFound; |
+ } |
+ |
+ /** |
+ * Gets the state completion percentage, taking into consideration the |
+ * |height| of the Contextual Search Panel, and the initial and final |
+ * states. A completion of 0 means the Panel is in the initial state and a |
+ * completion of 1 means the Panel is in the final state. |
+ * |
+ * @param height The height of the Contextual Search Panel. |
+ * @param startState The initial state of the Panel. |
+ * @param endState The final state of the Panel. |
+ * @return The completion percentage. |
+ */ |
+ private float getStateCompletion(float height, PanelState startState, PanelState endState) { |
+ float startSize = getPanelHeightFromState(startState); |
+ float endSize = getPanelHeightFromState(endState); |
+ float percentage = (height - startSize) / (endSize - startSize); |
+ return percentage; |
+ } |
+ |
+ /** |
+ * Updates the UI state for the closed to peeked transition (and vice |
+ * versa), according to a completion |percentage|. |
+ * |
+ * @param percentage The completion percentage. |
+ */ |
+ private void updatePanelForCloseOrPeek(float percentage) { |
+ // Base page offset. |
+ mBasePageY = 0.f; |
+ |
+ // Base page brightness. |
+ mBasePageBrightness = BASE_PAGE_BRIGHTNESS_STATE_PEEKED; |
+ |
+ // Search Bar height. |
+ mSearchBarHeight = mSearchBarHeightPeeking; |
+ |
+ // Search Bar border. |
+ mIsSearchBarBorderVisible = false; |
+ |
+ // Search Bar text opacity. |
+ mSearchBarTextOpacity = 1.f; |
+ |
+ // Search provider icon opacity. |
+ mSearchProviderIconOpacity = SEARCH_PROVIDER_ICON_OPACITY_STATE_PEEKED; |
+ |
+ // Search icon opacity. |
+ mSearchIconOpacity = SEARCH_ICON_OPACITY_PEEKED; |
+ |
+ // Progress Bar. |
+ mProgressBarOpacity = 0.f; |
+ } |
+ |
+ /** |
+ * Updates the UI state for the peeked to expanded transition (and vice |
+ * versa), according to a completion |percentage|. |
+ * |
+ * @param percentage The completion percentage. |
+ */ |
+ private void updatePanelForExpansion(float percentage) { |
+ // Base page offset. |
+ float baseBaseY = MathUtils.interpolate( |
+ 0.f, |
+ getBasePageTargetY(), |
+ percentage); |
+ mBasePageY = baseBaseY; |
+ |
+ // Base page brightness. |
+ float brightness = MathUtils.interpolate( |
+ BASE_PAGE_BRIGHTNESS_STATE_PEEKED, |
+ BASE_PAGE_BRIGHTNESS_STATE_EXPANDED, |
+ percentage); |
+ mBasePageBrightness = brightness; |
+ |
+ // Search Bar height. |
+ float searchBarHeight = Math.round(MathUtils.interpolate( |
+ mSearchBarHeightPeeking, |
+ mSearchBarHeightExpanded, |
+ percentage)); |
+ mSearchBarHeight = searchBarHeight; |
+ |
+ // Search Bar text opacity. |
+ mSearchBarTextOpacity = 1.f; |
+ |
+ // Search Bar border. |
+ mIsSearchBarBorderVisible = true; |
+ mSearchBarBorderY = searchBarHeight - SEARCH_BAR_BORDER_HEIGHT_DP + 1; |
+ |
+ // Search provider icon opacity. |
+ mSearchProviderIconOpacity = SEARCH_PROVIDER_ICON_OPACITY_STATE_PEEKED; |
+ |
+ // Search icon opacity. |
+ mSearchIconOpacity = SEARCH_ICON_OPACITY_PEEKED; |
+ |
+ // Progress Bar. |
+ float peekedHeight = getPanelHeightFromState(PanelState.PEEKED); |
+ float threshold = PROGRESS_BAR_VISIBILITY_THRESHOLD_DP / mPxToDp; |
+ float diff = Math.min(mHeight - peekedHeight, threshold); |
+ // Fades the Progress Bar the closer it gets to the bottom of the |
+ // screen. |
+ float progressBarOpacity = MathUtils.interpolate(0.f, 1.f, diff / threshold); |
+ mProgressBarOpacity = progressBarOpacity; |
+ mProgressBarY = searchBarHeight - PROGRESS_BAR_HEIGHT_DP + 1; |
+ } |
+ |
+ /** |
+ * Updates the UI state for the expanded to maximized transition (and vice |
+ * versa), according to a completion |percentage|. |
+ * |
+ * @param percentage The completion percentage. |
+ */ |
+ private void updatePanelForMaximization(float percentage) { |
+ // Base page offset. |
+ mBasePageY = getBasePageTargetY(); |
+ |
+ // Base page brightness. |
+ float brightness = MathUtils.interpolate( |
+ BASE_PAGE_BRIGHTNESS_STATE_EXPANDED, |
+ BASE_PAGE_BRIGHTNESS_STATE_MAXIMIZED, |
+ percentage); |
+ mBasePageBrightness = brightness; |
+ |
+ // Search Bar height. |
+ float searchBarHeight = Math.round(MathUtils.interpolate( |
+ mSearchBarHeightExpanded, |
+ mSearchBarHeightMaximized, |
+ percentage)); |
+ mSearchBarHeight = searchBarHeight; |
+ |
+ // Search Bar border. |
+ mIsSearchBarBorderVisible = true; |
+ mSearchBarBorderY = searchBarHeight - SEARCH_BAR_BORDER_HEIGHT_DP + 1; |
+ |
+ // Search Bar text opacity. |
+ mSearchBarTextOpacity = 1.f; |
+ |
+ // Search provider icon opacity. |
+ float searchProviderIconOpacity = MathUtils.interpolate( |
+ SEARCH_PROVIDER_ICON_OPACITY_STATE_EXPANDED, |
+ SEARCH_PROVIDER_ICON_OPACITY_STATE_MAXIMIZED, |
+ percentage); |
+ mSearchProviderIconOpacity = searchProviderIconOpacity; |
+ |
+ // Search icon opacity. |
+ float searchIconOpacity = MathUtils.interpolate( |
+ SEARCH_ICON_OPACITY_EXPANDED, |
+ SEARCH_ICON_OPACITY_MAXIMIZED, |
+ percentage); |
+ mSearchIconOpacity = searchIconOpacity; |
+ |
+ // Progress Bar. |
+ mProgressBarOpacity = 1.f; |
+ mProgressBarY = searchBarHeight - PROGRESS_BAR_HEIGHT_DP + 1; |
+ } |
+ |
+ /** |
+ * Updates the UI state for the peeked to promo transition (and vice versa), |
+ * according to a completion |percentage|. |
+ * |
+ * @param percentage The completion percentage. |
+ */ |
+ private void updatePanelForOpeningPromo(float percentage) { |
+ // Base page offset. |
+ float baseBaseY = MathUtils.interpolate( |
+ 0, |
+ getPromoBasePageOffsetY(), |
+ percentage); |
+ mBasePageY = baseBaseY; |
+ |
+ // Base page brightness. |
+ float brightness = MathUtils.interpolate( |
+ BASE_PAGE_BRIGHTNESS_STATE_PEEKED, |
+ BASE_PAGE_BRIGHTNESS_STATE_EXPANDED, |
+ percentage); |
+ mBasePageBrightness = brightness; |
+ |
+ // Search Bar height. |
+ float searchBarHeight = Math.round(MathUtils.interpolate( |
+ mSearchBarHeightPeeking, |
+ mSearchBarHeightPromo, |
+ percentage)); |
+ mSearchBarHeight = searchBarHeight; |
+ |
+ // Search Bar border. |
+ mIsSearchBarBorderVisible = false; |
+ |
+ // Search Bar text opacity. |
+ float peekedHeight = getPanelHeightFromState(PanelState.PEEKED); |
+ float threshold = DISAPPEARING_ELEMENT_THRESHOLD_DP / mPxToDp; |
+ float diff = Math.min(mHeight - peekedHeight, threshold); |
+ float disappearingElementOpacity = MathUtils.interpolate(1.f, 0.f, diff / threshold); |
+ mSearchBarTextOpacity = disappearingElementOpacity; |
+ |
+ // Search provider icon opacity. |
+ boolean shouldDisplaySearchProviderIcon = shouldHidePromoHeader(); |
+ mSearchProviderIconOpacity = |
+ shouldDisplaySearchProviderIcon ? 1.f : disappearingElementOpacity; |
+ |
+ // Search icon opacity. |
+ mSearchIconOpacity = SEARCH_ICON_OPACITY_PEEKED; |
+ |
+ // Progress Bar. |
+ mProgressBarOpacity = 0.f; |
+ mProgressBarY = searchBarHeight - PROGRESS_BAR_HEIGHT_DP + 1; |
+ } |
+ |
+ // ============================================================================================ |
+ // Base Page Offset |
+ // ============================================================================================ |
+ |
+ /** |
+ * Updates the coordinate of the existing selection. |
+ * @param y The y coordinate of the selection in pixels. |
+ */ |
+ protected void updateBasePageSelectionYPx(float y) { |
+ mBasePageSelectionYPx = y; |
+ updateBasePageTargetY(); |
+ } |
+ |
+ /** |
+ * Updates the target offset of the Base Page in order to keep the selection in view |
+ * after expanding the Panel. |
+ */ |
+ private void updateBasePageTargetY() { |
+ mBasePageTargetY = calculateBasePageTargetY(PanelState.EXPANDED); |
+ mPromoBasePageTargetY = calculateBasePageTargetY(PanelState.PROMO); |
+ } |
+ |
+ /** |
+ * Calculates the target offset of the Base Page in order to keep the selection in view |
+ * after expanding the Panel to the given |expandedState|. |
+ * |
+ * @param expandedState |
+ * @return The target offset Y. |
+ */ |
+ private float calculateBasePageTargetY(PanelState expandedState) { |
+ // Convert from px to dp. |
+ final float selectionY = mBasePageSelectionYPx * mPxToDp; |
+ |
+ // Calculate the exact height of the expanded Panel without taking into |
+ // consideration the height of the shadow (what is returned by the |
+ // getPanelFromHeight method). We need the measurement of the portion |
+ // of the Panel that occludes the page. |
+ final float expandedHeight = getPanelHeightFromState(expandedState) |
+ - PANEL_SHADOW_HEIGHT_DP; |
+ |
+ // Calculate the offset to center the selection on the available area. |
+ final float fullscreenHeight = getFullscreenHeight(); |
+ final float availableHeight = fullscreenHeight - expandedHeight; |
+ float offset = -selectionY + availableHeight / 2; |
+ |
+ // Make sure offset is negative to prevent Base Page from moving down, |
+ // because there's nothing to render above the Page. |
+ offset = Math.min(offset, 0.f); |
+ // If visible, the Toolbar will be hidden. Therefore, we need to adjust |
+ // the offset to account for this difference. |
+ if (mIsToolbarShowing) |
+ offset -= TOOLBAR_HEIGHT_DP; |
+ // Make sure the offset is not greater than the expanded height, because |
+ // there's nothing to render below the Page. |
+ offset = Math.max(offset, -expandedHeight); |
+ |
+ return offset; |
+ } |
+ |
+ /** |
+ * @return The Y coordinate to apply to the Base Page in order to keep the selection |
+ * in view when the Search Panel is in EXPANDED state. |
+ */ |
+ public float getBasePageTargetY() { |
+ return mBasePageTargetY; |
+ } |
+ |
+ /** |
+ * @return The Y coordinate to apply to the Base Page in order to keep the |
+ * selection in view when the Search Panel is in PROMO state. |
+ */ |
+ private float getPromoBasePageOffsetY() { |
+ return mPromoBasePageTargetY; |
+ } |
+ |
+ // ============================================================================================ |
+ // Promo |
+ // ============================================================================================ |
+ |
+ /** |
+ * Updates the UI state for a given |height| when performing the opt-in animation. |
+ * |
+ * @param height The Contextual Search Panel height. |
+ */ |
+ protected void setPanelHeightForPromoOptInAnimation(float height) { |
+ PanelState endState = PanelState.EXPANDED; |
+ PanelState startState = PanelState.PROMO; |
+ float percentage = getStateCompletion(height, startState, endState); |
+ |
+ updatePanelSize(height, endState, percentage); |
+ |
+ updatePanelForPromoOptInAnimation(percentage); |
+ } |
+ |
+ /** |
+ * Updates the UI state for the PROMO to EXPANDED transition (and vice versa), |
+ * according to a completion |percentage|. |
+ * |
+ * @param percentage The completion percentage. |
+ */ |
+ private void updatePanelForPromoOptInAnimation(float percentage) { |
+ // Base page offset. |
+ float baseBaseY = MathUtils.interpolate( |
+ getPromoBasePageOffsetY(), |
+ getBasePageTargetY(), |
+ percentage); |
+ mBasePageY = baseBaseY; |
+ |
+ // Base page brightness. |
+ mBasePageBrightness = BASE_PAGE_BRIGHTNESS_STATE_EXPANDED; |
+ |
+ // Search Bar height. |
+ float searchBarHeight = Math.round(MathUtils.interpolate( |
+ mSearchBarHeightPromo, |
+ mSearchBarHeightExpanded, |
+ percentage)); |
+ mSearchBarHeight = searchBarHeight; |
+ |
+ // Search Bar border. |
+ mIsSearchBarBorderVisible = false; |
+ |
+ // Search Bar text opacity. |
+ final float threshold = 0.5f; |
+ float searchBarTextOpacity = MathUtils.interpolate(0.f, 1.f, |
+ Math.max(percentage - threshold, 0) / threshold); |
+ mSearchBarTextOpacity = searchBarTextOpacity; |
+ |
+ // Search provider icon opacity. |
+ boolean shouldDisplaySearchProviderIcon = shouldHidePromoHeader(); |
+ mSearchProviderIconOpacity = shouldDisplaySearchProviderIcon |
+ ? 1.f : MathUtils.interpolate(0.f, 1.f, percentage); |
+ |
+ // Search icon opacity. |
+ mSearchIconOpacity = SEARCH_ICON_OPACITY_PEEKED; |
+ |
+ // Progress Bar. |
+ mProgressBarOpacity = percentage == 1.f ? 1.f : 0.f; |
+ mProgressBarY = searchBarHeight - PROGRESS_BAR_HEIGHT_DP + 1; |
+ } |
+} |