// 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 android.view.ViewGroup; import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelAnimation; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelInflater; import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation; import org.chromium.chrome.browser.util.MathUtils; import org.chromium.ui.resources.dynamics.DynamicResourceLoader; /** * Controls the Search Peek Promo. */ public class ContextualSearchPeekPromoControl extends OverlayPanelInflater implements ChromeAnimation.Animatable<ContextualSearchPeekPromoControl.AnimationType> { /** * The initial width of the ripple for the appearance animation, in dps. */ private static final float RIPPLE_MINIMUM_WIDTH_DP = 56.f; /** * Animation properties. */ protected enum AnimationType { APPEARANCE } /** * Whether the Peek Promo is visible. */ private boolean mIsVisible; /** * The height of the Peek Promo, in pixels. */ private float mHeightPx; /** * The width of the Ripple resource in pixels. */ private float mRippleWidthPx; /** * The opacity of the Ripple resource. */ private float mRippleOpacity; /** * The opacity of the Promo Text View dynamic resource. */ private float mTextOpacity; /** * The precomputed padding of the Peek Promo, in pixels. */ private final float mPaddingPx; /** * The precomputed default height of the Peek Promo in pixels. */ private final float mDefaultHeightPx; /** * The precomputed minimum width of the Ripple resource in pixels. */ private final float mRippleMinimumWidthPx; /** * The precomputed maximum width of the Ripple resource in pixels. */ private final float mRippleMaximumWidthPx; /** * @param panel The panel. * @param context The Android Context used to inflate the View. * @param container The container View used to inflate the View. * @param resourceLoader The resource loader that will handle the snapshot capturing. */ public ContextualSearchPeekPromoControl(OverlayPanel panel, Context context, ViewGroup container, DynamicResourceLoader resourceLoader) { super(panel, R.layout.contextual_search_peek_promo_text_view, R.id.contextual_search_peek_promo_text_view, context, container, resourceLoader); final float dpToPx = context.getResources().getDisplayMetrics().density; mDefaultHeightPx = context.getResources().getDimensionPixelOffset( R.dimen.contextual_search_peek_promo_height); mPaddingPx = context.getResources().getDimensionPixelOffset( R.dimen.contextual_search_peek_promo_padding); mRippleMinimumWidthPx = RIPPLE_MINIMUM_WIDTH_DP * dpToPx; mRippleMaximumWidthPx = panel.getMaximumWidthPx(); } /** * Shows the Peek Promo. This includes inflating the View and setting it to its initial state. * This also means a new cc::Layer will be created and added to the tree. */ void show() { if (mIsVisible) return; mIsVisible = true; mHeightPx = Math.round(mDefaultHeightPx); invalidate(); } /** * Hides the Peek Promo, returning the Control to its initial uninitialized state. In this * state, now View will be created and no Layer added to the tree (or removed if present). */ void hide() { if (!mIsVisible) return; mIsVisible = false; mHeightPx = 0.f; } /** * @return The height of the Peek Promo when the Panel is the peeked state. */ float getHeightPeekingPx() { return mIsVisible ? mDefaultHeightPx : 0.f; } // ============================================================================================ // Public API // ============================================================================================ /** * @return Whether the Peek Promo is visible. */ public boolean isVisible() { return mIsVisible; } /** * @return The Peek Promo height in pixels. */ public float getHeightPx() { return mHeightPx; } /** * @return The Peek Promo padding in pixels. */ public float getPaddingPx() { return mPaddingPx; } /** * @return The width of the Ripple resource in pixels. */ public float getRippleWidthPx() { return mRippleWidthPx; } /** * @return The opacity of the Ripple resource. */ public float getRippleOpacity() { return mRippleOpacity; } /** * @return The opacity of the Promo Text View dynamic resource. */ public float getTextOpacity() { return mTextOpacity; } // ============================================================================================ // Panel Animation // ============================================================================================ /** * Interpolates the UI from states Closed to Peeked. * * @param percentage The completion percentage. */ public void onUpdateFromCloseToPeek(float percentage) { if (!isVisible()) return; mHeightPx = Math.round(mDefaultHeightPx); } /** * Interpolates the UI from states Peeked to Expanded. * * @param percentage The completion percentage. */ public void onUpdateFromPeekToExpand(float percentage) { if (!isVisible()) return; mHeightPx = Math.round(MathUtils.interpolate(mDefaultHeightPx, 0.f, percentage)); mTextOpacity = MathUtils.interpolate(1.f, 0.f, percentage); } /** * Interpolates the UI from states Expanded to Maximized. * * @param percentage The completion percentage. */ public void onUpdateFromExpandToMaximize(float percentage) { if (!isVisible()) return; mHeightPx = 0.f; mTextOpacity = 0.f; } // ============================================================================================ // Peek Promo Appearance Animation // ============================================================================================ /** * Animates the Peek Promo appearance. */ public void animateAppearance() { // TODO(pedrosimonetti): Find a generic way to tell when a specific animation finishes. mOverlayPanel.addToAnimation(this, AnimationType.APPEARANCE, 0.f, 1.f, OverlayPanelAnimation.BASE_ANIMATION_DURATION_MS, 0); } @Override public void setProperty(AnimationType type, float value) { if (type == AnimationType.APPEARANCE) { updateForAppearanceAnimation(value); } } @Override public void onPropertyAnimationFinished(AnimationType prop) {} /** * Updates the UI for the appearance animation. * * @param percentage The completion percentage. */ private void updateForAppearanceAnimation(float percentage) { mRippleWidthPx = Math.round(MathUtils.interpolate( mRippleMinimumWidthPx, mRippleMaximumWidthPx, percentage)); mRippleOpacity = MathUtils.interpolate(0.f, 1.f, percentage); float textOpacityDelay = 0.5f; float textOpacityPercentage = Math.max(0, percentage - textOpacityDelay) / (1.f - textOpacityDelay); mTextOpacity = MathUtils.interpolate(0.f, 1.f, textOpacityPercentage); } }