// 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.app.Activity;
import android.content.Context;
import android.view.View.MeasureSpec;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ApplicationStatus.ActivityStateListener;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.compositor.LayerTitleCache;
import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.PanelPriority;
import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEventFilter.ScrollDirection;
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.compositor.layouts.eventfilter.GestureHandler;
import org.chromium.chrome.browser.compositor.layouts.eventfilter.OverlayPanelEventFilter;
import org.chromium.chrome.browser.compositor.overlays.SceneOverlay;
import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.content.browser.ContentViewClient;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_public.common.TopControlsState;
import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.resources.ResourceManager;
import java.util.List;
/**
* Controls the Overlay Panel.
*/
public class OverlayPanel extends OverlayPanelAnimation implements ActivityStateListener,
EdgeSwipeHandler, GestureHandler, OverlayPanelContentFactory, SceneOverlay {
/** The extra dp added around the close button touch target. */
private static final int CLOSE_BUTTON_TOUCH_SLOP_DP = 5;
/** State of the Overlay Panel. */
public static enum PanelState {
// TODO(pedrosimonetti): consider removing the UNDEFINED state
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 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,
SUPPRESS,
UNSUPPRESS,
FULLSCREEN_ENTERED,
FULLSCREEN_EXITED,
INFOBAR_SHOWN,
INFOBAR_HIDDEN,
CONTENT_CHANGED,
KEYBOARD_SHOWN,
KEYBOARD_HIDDEN,
TAB_NAVIGATION
}
/** The activity this panel is in. */
protected ChromeActivity mActivity;
/** The initial height of the Overlay Panel. */
private float mInitialPanelHeight;
/** The initial location of a touch on the panel */
private float mInitialPanelTouchY;
/** Whether a touch gesture has been detected. */
private boolean mHasDetectedTouchGesture;
/** The EventFilter that this panel uses. */
protected EventFilter mEventFilter;
/** That factory that creates OverlayPanelContents. */
private OverlayPanelContentFactory mContentFactory;
/** Container for content the panel will show. */
private OverlayPanelContent mContent;
/** OverlayPanel manager handle for notifications of opening and closing. */
protected OverlayPanelManager mPanelManager;
/** If the base page text controls have been cleared. */
private boolean mDidClearTextControls;
/** If the panel should be ignoring swipe events (for compatibility mode). */
private boolean mIgnoreSwipeEvents;
/** This is used to make sure there is one show request to one close request. */
private boolean mPanelShown;
/**
* Cache the viewport width and height of the screen to filter SceneOverlay#onSizeChanged
* events.
*/
private float mViewportWidth;
private float mViewportHeight;
// ============================================================================================
// Constructor
// ============================================================================================
/**
* @param context The current Android {@link Context}.
* @param updateHost The {@link LayoutUpdateHost} used to request updates in the Layout.
* @param eventHost The {@link EventFilterHost} used to propagate events.
* @param panelManager The {@link OverlayPanelManager} responsible for showing panels.
*/
public OverlayPanel(Context context, LayoutUpdateHost updateHost, EventFilterHost eventHost,
OverlayPanelManager panelManager) {
super(context, updateHost);
mContentFactory = this;
mPanelManager = panelManager;
mPanelManager.registerPanel(this);
mEventFilter = new OverlayPanelEventFilter(mContext, eventHost, this);
}
/**
* Destroy the native components associated with this panel's content.
*/
public void destroy() {
closePanel(StateChangeReason.UNKNOWN, false);
ApplicationStatus.unregisterActivityStateListener(this);
}
/**
* Destroy the components associated with this panel. This should be overridden by panel
* implementations to destroy text views and other elements.
*/
protected void destroyComponents() {
destroyOverlayPanelContent();
}
@Override
protected void onClosed(StateChangeReason reason) {
mPanelShown = false;
setBasePageTextControlsVisibility(true);
destroyComponents();
mPanelManager.notifyPanelClosed(this, reason);
}
// ============================================================================================
// 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);
}
@Override
public void closePanel(StateChangeReason reason, boolean animate) {
// If the panel hasn't peeked, then it shouldn't need to close.
if (!mPanelShown) return;
super.closePanel(reason, animate);
}
/**
* Request that this panel be shown.
* @param reason The reason the panel is being shown.
*/
public void requestPanelShow(StateChangeReason reason) {
if (mPanelShown) return;
if (mPanelManager != null) {
mPanelManager.requestPanelShow(this, reason);
}
}
@Override
public void peekPanel(StateChangeReason reason) {
// TODO(mdjones): This is making a protected API public and should be removed. Animation
// should only be controlled by the OverlayPanelManager.
// Since the OverlayPanelManager can show panels without requestPanelShow being called, the
// flag for the panel being shown should be set to true here.
mPanelShown = true;
super.peekPanel(reason);
}
/**
* @param url The URL that the panel should load.
*/
public void loadUrlInPanel(String url) {
getOverlayPanelContent().loadUrl(url, true);
}
/**
* @param url The URL that the panel should load.
* @param shouldLoadImmediately If the URL should be loaded immediately when this method is
* called.
*/
public void loadUrlInPanel(String url, boolean shouldLoadImmediately) {
getOverlayPanelContent().loadUrl(url, shouldLoadImmediately);
}
/**
* @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;
if (mActivity != null) {
ApplicationStatus.registerStateListenerForActivity(this, mActivity);
}
// TODO(pedrosimonetti): Coordinate with mdjones@ to move this to the OverlayPanelBase
// constructor, once we are able to get the Activity during instantiation. The Activity
// is needed in order to get the correct height of the Toolbar, which varies depending
// on the Activity (WebApps have a smaller toolbar for example).
initializeUiState();
}
/**
* Notify the panel's content that it has been touched.
* @param x The X position of the touch in dp.
*/
public void notifyBarTouched(float x) {
if (!isCoordinateInsideCloseButton(x)) {
getOverlayPanelContent().showContent();
}
}
/**
* Acknowledges that there was a touch in the search content view, though no immediate action
* needs to be taken. This should be overridden by child classes.
* TODO(mdjones): Get a better name for this.
*/
public void onTouchSearchContentViewAck() {
}
/**
* Get a panel's display priority. This has a default to MEDIUM and should be overridden by
* child classes.
* @return The panel's display priority.
*/
public PanelPriority getPriority() {
return PanelPriority.MEDIUM;
}
/**
* @return True if a panel can be suppressed. This should be overridden by each panel.
*/
public boolean canBeSuppressed() {
return false;
}
/**
* @return The absolute amount in DP that the top controls have shifted off screen.
*/
protected float getTopControlsOffsetDp() {
if (mActivity == null || mActivity.getFullscreenManager() == null) return 0.0f;
return -mActivity.getFullscreenManager().getControlOffset() * mPxToDp;
}
/**
* Set the visibility of the base page text selection controls. This will also attempt to
* remove focus from the base page to clear any open controls.
* TODO(mdjones): This should be replaced with focusing the panel's ContentViewCore.
* @param visible If the text controls are visible.
*/
protected void setBasePageTextControlsVisibility(boolean visible) {
if (mActivity == null || mActivity.getActivityTab() == null) return;
ContentViewCore baseContentView = mActivity.getActivityTab().getContentViewCore();
if (baseContentView == null) return;
// If the panel does not have focus or isn't open, return.
if (isPanelOpened() && mDidClearTextControls && !visible) return;
if (!isPanelOpened() && !mDidClearTextControls && visible) return;
mDidClearTextControls = !visible;
if (!visible) {
baseContentView.preserveSelectionOnNextLossOfFocus();
baseContentView.getContainerView().clearFocus();
}
baseContentView.updateTextSelectionUI(visible);
}
// ============================================================================================
// ActivityStateListener
// ============================================================================================
@Override
public void onActivityStateChange(Activity activity, int newState) {
boolean isMultiWindowMode = MultiWindowUtils.getInstance().isLegacyMultiWindow(mActivity)
|| MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity);
// In multi-window mode the activity that was interacted with last is resumed and
// all others are paused. We should not close Contextual Search in this case,
// because the activity may be visible even though it is paused.
if (isMultiWindowMode
&& (newState == ActivityState.PAUSED || newState == ActivityState.RESUMED)) {
return;
}
if (newState == ActivityState.RESUMED
|| newState == ActivityState.STOPPED
|| newState == ActivityState.DESTROYED) {
closePanel(StateChangeReason.UNKNOWN, false);
}
}
// ============================================================================================
// 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.
*/
@Override
public OverlayPanelContent createNewOverlayPanelContent() {
return new OverlayPanelContent(new OverlayContentDelegate(),
new OverlayContentProgressObserver(), mActivity);
}
/**
* Add any other objects that depend on the OverlayPanelContent having already been created.
*/
private OverlayPanelContent createNewOverlayPanelContentInternal() {
OverlayPanelContent content = mContentFactory.createNewOverlayPanelContent();
content.setContentViewClient(new ContentViewClient() {
@Override
public int getDesiredWidthMeasureSpec() {
if (isFullWidthSizePanel()) {
return super.getDesiredWidthMeasureSpec();
} else {
return MeasureSpec.makeMeasureSpec(
getContentViewWidthPx(),
MeasureSpec.EXACTLY);
}
}
@Override
public int getDesiredHeightMeasureSpec() {
if (isFullWidthSizePanel()) {
return super.getDesiredHeightMeasureSpec();
} else {
return MeasureSpec.makeMeasureSpec(
getContentViewHeightPx(),
MeasureSpec.EXACTLY);
}
}
});
return content;
}
/**
* @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 = createNewOverlayPanelContentInternal();
}
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;
}
}
/**
* Updates the top controls state for the base tab. As these values are set at the renderer
* level, there is potential for this impacting other tabs that might share the same
* process. See {@link Tab#updateTopControlsState(int current, boolean animate)}
* @param current The desired current state for the controls. Pass
* {@link TopControlsState#BOTH} to preserve the current position.
* @param animate Whether the controls should animate to the specified ending condition or
* should jump immediately.
*/
public void updateTopControlsState(int current, boolean animate) {
Tab currentTab = mActivity.getActivityTab();
if (currentTab != null) {
currentTab.updateTopControlsState(current, animate);
}
}
/**
* Sets the top control state based on the internals of the panel.
*/
public void updateTopControlsState() {
if (mContent == null) return;
if (isFullWidthSizePanel()) {
// 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 != null ? mContent.getContentVerticalScroll() : 0.0f;
}
// ============================================================================================
// OverlayPanelBase methods.
// ============================================================================================
@Override
protected void onAnimationFinished() {
super.onAnimationFinished();
if (getPanelState() == PanelState.PEEKED || getPanelState() == PanelState.CLOSED) {
setBasePageTextControlsVisibility(true);
} else {
setBasePageTextControlsVisibility(false);
}
}
@Override
protected int getControlContainerHeightResource() {
// TODO(mdjones): Investigate passing this in to the constructor instead.
assert mActivity != null;
return mActivity.getControlContainerHeightResource();
}
// ============================================================================================
// Layout Integration
// ============================================================================================
/**
* Updates the Panel so it preserves its state when the orientation changes.
*/
protected void updatePanelForOrientationChange() {
resizePanelToState(getPanelState(), StateChangeReason.UNKNOWN);
}
// ============================================================================================
// 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 (mContent != null && 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);
}
/**
* @param x The x coordinate in dp.
* @return Whether the given |x| coordinate is inside the close button.
*/
protected boolean isCoordinateInsideCloseButton(float x) {
if (LocalizationUtils.isLayoutRtl()) {
return x <= (getCloseIconX() + getCloseIconDimension() + CLOSE_BUTTON_TOUCH_SLOP_DP);
} else {
return x >= (getCloseIconX() - CLOSE_BUTTON_TOUCH_SLOP_DP);
}
}
/**
* 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()) {
expandPanel(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;
}
/**
* If the panel is intercepting the initial bar swipe event. This should be overridden per
* panel.
* @return True if the panel intercepted the initial bar swipe.
*/
public boolean onInterceptBarSwipe() {
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() + getBarContainerHeight());
}
/**
* @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() + getBarContainerHeight();
}
/**
* @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.
*/
public 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;
}
// ============================================================================================
// GestureHandler and EdgeSwipeHandler implementation.
// ============================================================================================
@Override
public void onDown(float x, float y, boolean fromMouse, int buttons) {
mInitialPanelTouchY = y;
handleSwipeStart();
}
@Override
public void drag(float x, float y, float deltaX, float deltaY, float tx, float ty) {
handleSwipeMove(y - mInitialPanelTouchY);
}
@Override
public void onUpOrCancel() {
handleSwipeEnd();
}
@Override
public void fling(float x, float y, float velocityX, float velocityY) {
handleFling(velocityY);
}
@Override
public void click(float x, float y, boolean fromMouse, int buttons) {
// TODO(mdjones): The time param for handleClick is not used anywhere, remove it.
handleClick(0, x, y);
}
@Override
public void onLongPress(float x, float y) {}
@Override
public void onPinch(float x0, float y0, float x1, float y1, boolean firstEvent) {}
// EdgeSwipeHandler implementation.
@Override
public void swipeStarted(ScrollDirection direction, float x, float y) {
if (onInterceptBarSwipe()) {
mIgnoreSwipeEvents = true;
return;
}
handleSwipeStart();
}
@Override
public void swipeUpdated(float x, float y, float dx, float dy, float tx, float ty) {
if (mIgnoreSwipeEvents) return;
handleSwipeMove(ty);
}
@Override
public void swipeFinished() {
if (mIgnoreSwipeEvents) {
mIgnoreSwipeEvents = false;
return;
}
handleSwipeEnd();
}
@Override
public void swipeFlingOccurred(float x, float y, float tx, float ty, float vx, float vy) {
if (mIgnoreSwipeEvents) return;
handleFling(vy);
}
@Override
public boolean isSwipeEnabled(ScrollDirection direction) {
return direction == ScrollDirection.UP && isShowing();
}
// ============================================================================================
// SceneOverlay implementation.
// ============================================================================================
@Override
public SceneOverlayLayer getUpdatedSceneOverlayTree(LayerTitleCache layerTitleCache,
ResourceManager resourceManager, float yOffset) {
return null;
}
@Override
public boolean isSceneOverlayTreeShowing() {
return isShowing();
}
@Override
public EventFilter getEventFilter() {
return mEventFilter;
}
@Override
public void onSizeChanged(float width, float height, float visibleViewportOffsetY,
int orientation) {
// Filter events that don't change the viewport width or height.
if (height != mViewportHeight || width != mViewportWidth) {
// We only care if the orientation is changing or we're shifting in/out of multi-window.
// In either case the screen's viewport width or height will certainly change.
mViewportWidth = width;
mViewportHeight = height;
resizePanelContentViewCore(width, height);
onLayoutChanged(width, height, visibleViewportOffsetY);
}
}
/**
* Resize the panel's ContentViewCore manually since it is not attached to the view hierarchy.
* TODO(mdjones): Remove the need for this method by supporting multiple ContentViewCores
* existing simultaneously in the view hierarchy.
* @param width The new width in dp.
* @param height The new height in dp.
*/
protected void resizePanelContentViewCore(float width, float height) {
if (!isShowing()) return;
ContentViewCore panelContent = getContentViewCore();
if (panelContent != null) {
panelContent.onSizeChanged((int) (width / mPxToDp),
(int) (height / mPxToDp), panelContent.getViewportWidthPix(),
panelContent.getViewportHeightPix());
panelContent.onPhysicalBackingSizeChanged(
(int) (width / mPxToDp), (int) (height / mPxToDp));
}
}
@Override
public void getVirtualViews(List<VirtualView> views) {
// TODO(mdjones): Add views for accessibility.
}
@Override
public boolean handlesTabCreating() {
// If the panel is not opened, do not handle tab creating.
if (!isPanelOpened()) return false;
// Updates TopControls' State so the Toolbar becomes visible.
// TODO(pedrosimonetti): The transition when promoting to a new tab is only smooth
// if the SearchContentView's vertical scroll position is zero. Otherwise the
// ContentView will appear to jump in the screen. Coordinate with @dtrainor to solve
// this problem.
updateTopControlsState(TopControlsState.BOTH, false);
return true;
}
@Override
public boolean shouldHideAndroidTopControls() {
return isPanelOpened();
}
@Override
public boolean updateOverlay(long time, long dt) {
if (isPanelOpened()) setBasePageTextControlsVisibility(false);
return super.onUpdateAnimation(time, false);
}
@Override
public void onHideLayout() {
if (!isShowing()) return;
closePanel(StateChangeReason.UNKNOWN, false);
}
@Override
public boolean onBackPressed() {
if (!isPanelOpened()) return false;
closePanel(StateChangeReason.BACK_PRESS, true);
return true;
}
@Override
public void tabTitleChanged(int tabId, String title) {}
@Override
public void tabStateInitialized() {}
@Override
public void tabModelSwitched(boolean incognito) {}
@Override
public void tabSelected(long time, boolean incognito, int id, int prevId) {}
@Override
public void tabMoved(long time, boolean incognito, int id, int oldIndex, int newIndex) {}
@Override
public void tabClosed(long time, boolean incognito, int id) {}
@Override
public void tabClosureCancelled(long time, boolean incognito, int id) {}
@Override
public void tabCreated(long time, boolean incognito, int id, int prevId, boolean selected) {}
@Override
public void tabPageLoadStarted(int id, boolean incognito) {}
@Override
public void tabPageLoadFinished(int id, boolean incognito) {}
@Override
public void tabLoadStarted(int id, boolean incognito) {}
@Override
public void tabLoadFinished(int id, boolean incognito) {}
}