| // 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.content.Context; |
| import android.graphics.PointF; |
| import android.graphics.RectF; |
| import android.view.MotionEvent; |
| import android.view.ViewGroup; |
| |
| import androidx.annotation.IntDef; |
| import androidx.annotation.VisibleForTesting; |
| |
| import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; |
| import org.chromium.chrome.browser.compositor.LayerTitleCache; |
| import org.chromium.chrome.browser.compositor.animation.CompositorAnimationHandler; |
| 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.EventFilter; |
| import org.chromium.chrome.browser.compositor.overlays.SceneOverlay; |
| import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer; |
| import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
| import org.chromium.ui.resources.ResourceManager; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Abstract representation of an OpenGL layout tailored to draw tabs. It is a framework used as an |
| * alternative to the Android UI for lower level hardware accelerated rendering. |
| * This layout also pass through all the events that may happen. |
| */ |
| |
| public abstract class Layout implements TabContentManager.ThumbnailChangeListener { |
| /** |
| * The orientation of the device. |
| */ |
| @IntDef({Orientation.UNSET, Orientation.PORTRAIT, Orientation.LANDSCAPE}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface Orientation { |
| int UNSET = 0; |
| int PORTRAIT = 1; |
| int LANDSCAPE = 2; |
| } |
| |
| /** The possible variations of the visible viewport that different layouts may need. */ |
| @IntDef({ViewportMode.ALWAYS_FULLSCREEN, ViewportMode.ALWAYS_SHOWING_BROWSER_CONTROLS, |
| ViewportMode.DYNAMIC_BROWSER_CONTROLS, |
| ViewportMode.USE_PREVIOUS_BROWSER_CONTROLS_STATE}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface ViewportMode { |
| /** The viewport is assumed to be always fullscreen. */ |
| int ALWAYS_FULLSCREEN = 0; |
| /** The viewport is assuming that browser controls are permanently shown. */ |
| int ALWAYS_SHOWING_BROWSER_CONTROLS = 1; |
| /** The viewport will account for animating browser controls (both shown and hidden). */ |
| int DYNAMIC_BROWSER_CONTROLS = 2; |
| /** Use a viewport that accounts for the browser controls state in the previous layout. */ |
| int USE_PREVIOUS_BROWSER_CONTROLS_STATE = 3; |
| } |
| |
| // Defines to make the code easier to read. |
| public static final boolean NEED_TITLE = true; |
| public static final boolean NO_TITLE = false; |
| public static final boolean SHOW_CLOSE_BUTTON = true; |
| public static final boolean NO_CLOSE_BUTTON = false; |
| |
| /** Length of the unstalling animation. **/ |
| public static final long UNSTALLED_ANIMATION_DURATION_MS = 500; |
| |
| private static final float SNAP_SPEED = 1.0f; // dp per second |
| |
| // Drawing area properties. |
| private float mWidthDp; |
| private float mHeightDp; |
| private float mTopBrowserControlsHeightDp; |
| private float mBottomBrowserControlsHeightDp; |
| |
| /** A {@link Context} instance. */ |
| private Context mContext; |
| |
| /** The current {@link Orientation} of the layout. */ |
| private @Orientation int mCurrentOrientation; |
| |
| // Tabs |
| protected TabModelSelector mTabModelSelector; |
| protected TabContentManager mTabContentManager; |
| |
| // Tablet tab strip managers. |
| private final List<SceneOverlay> mSceneOverlays = new ArrayList<SceneOverlay>(); |
| |
| // Helpers |
| private final LayoutUpdateHost mUpdateHost; |
| protected final LayoutRenderHost mRenderHost; |
| |
| /** The tabs currently being rendered as part of this layout. The tabs are |
| * drawn using the same ordering as this array. */ |
| protected LayoutTab[] mLayoutTabs; |
| |
| // True means that the layout is going to hide as soon as the animation finishes. |
| private boolean mIsHiding; |
| |
| // The next id to show when the layout is hidden, or TabBase#INVALID_TAB_ID if no change. |
| protected int mNextTabId = Tab.INVALID_TAB_ID; |
| |
| // The ratio of dp to px. |
| protected final float mDpToPx; |
| protected final float mPxToDp; |
| |
| /** |
| * The {@link Layout} is not usable until sizeChanged is called. |
| * This is convenient this way so we can pre-create the layout before the host is fully defined. |
| * @param context The current Android's context. |
| * @param updateHost The parent {@link LayoutUpdateHost}. |
| * @param renderHost The parent {@link LayoutRenderHost}. |
| */ |
| public Layout(Context context, LayoutUpdateHost updateHost, LayoutRenderHost renderHost) { |
| mContext = context; |
| mUpdateHost = updateHost; |
| mRenderHost = renderHost; |
| |
| // Invalid sizes |
| mWidthDp = -1; |
| mHeightDp = -1; |
| mTopBrowserControlsHeightDp = -1; |
| mBottomBrowserControlsHeightDp = -1; |
| |
| mCurrentOrientation = Orientation.UNSET; |
| mDpToPx = context.getResources().getDisplayMetrics().density; |
| mPxToDp = 1 / mDpToPx; |
| } |
| |
| /** |
| * @return The handler responsible for running compositor animations. |
| */ |
| public CompositorAnimationHandler getAnimationHandler() { |
| return mUpdateHost.getAnimationHandler(); |
| } |
| |
| /** |
| * Called when native initialization is completed. |
| */ |
| public void onFinishNativeInitialization() {} |
| |
| /** |
| * Adds a {@link SceneOverlay} that can be shown in this layout to the first position in the |
| * scene overlay list, meaning it will be drawn behind all other overlays. |
| * @param overlay The {@link SceneOverlay} to be added. |
| */ |
| void addSceneOverlayToBack(SceneOverlay overlay) { |
| assert !mSceneOverlays.contains(overlay); |
| mSceneOverlays.add(0, overlay); |
| } |
| |
| /** |
| * Adds a {@link SceneOverlay} that can potentially be shown on top of this {@link Layout}. The |
| * {@link SceneOverlay}s added to this {@link Layout} will be cascaded in the order they are |
| * added. The {@link SceneOverlay} added first will become the content of the |
| * {@link SceneOverlay} added second, and so on. |
| * @param helper A {@link SceneOverlay} to add as a potential overlay for this {@link Layout}. |
| */ |
| public void addSceneOverlay(SceneOverlay helper) { |
| assert !mSceneOverlays.contains(helper); |
| mSceneOverlays.add(helper); |
| } |
| |
| /** |
| * Cleans up any internal state. This object should not be used after this call. |
| */ |
| public void destroy() { |
| } |
| |
| /** |
| * @return The current {@link Context} instance associated with this {@link Layout}. |
| */ |
| public Context getContext() { |
| return mContext; |
| } |
| |
| /** |
| * @return Whether the {@link Layout} is currently active. |
| */ |
| public boolean isActive() { |
| return mUpdateHost.isActiveLayout(this); |
| } |
| |
| /** |
| * Get a list of virtual views for accessibility. |
| * |
| * @param views A List to populate with virtual views. |
| */ |
| public void getVirtualViews(List<VirtualView> views) { |
| // TODO(dtrainor): Investigate order. |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).getVirtualViews(views); |
| } |
| } |
| |
| /** |
| * Creates a {@link LayoutTab}. |
| * @param id The id of the reference {@link Tab} in the {@link TabModel}. |
| * @param isIncognito Whether the new tab is incognito. |
| * @param showCloseButton True to show and activate a close button on the border. |
| * @param isTitleNeeded Whether a title will be shown. |
| * @return The newly created {@link LayoutTab}. |
| */ |
| public LayoutTab createLayoutTab( |
| int id, boolean isIncognito, boolean showCloseButton, boolean isTitleNeeded) { |
| return createLayoutTab(id, isIncognito, showCloseButton, isTitleNeeded, -1.f, -1.f); |
| } |
| |
| /** |
| * Creates a {@link LayoutTab}. |
| * @param id The id of the reference {@link Tab} in the {@link TabModel}. |
| * @param isIncognito Whether the new tab is incognito. |
| * @param showCloseButton True to show and activate a close button on the border. |
| * @param isTitleNeeded Whether a title will be shown. |
| * @param maxContentWidth The max content width of the tab. Negative numbers will use the |
| * original content width. |
| * @param maxContentHeight The max content height of the tab. Negative numbers will use the |
| * original content height. |
| * @return The newly created {@link LayoutTab}. |
| */ |
| public LayoutTab createLayoutTab(int id, boolean isIncognito, boolean showCloseButton, |
| boolean isTitleNeeded, float maxContentWidth, float maxContentHeight) { |
| LayoutTab layoutTab = mUpdateHost.createLayoutTab( |
| id, isIncognito, showCloseButton, isTitleNeeded, maxContentWidth, maxContentHeight); |
| initLayoutTabFromHost(layoutTab); |
| return layoutTab; |
| } |
| |
| /** |
| * Releases the data we keep for that {@link LayoutTab}. |
| * @param layoutTab The {@link LayoutTab} to release. |
| */ |
| public void releaseTabLayout(LayoutTab layoutTab) { |
| mUpdateHost.releaseTabLayout(layoutTab.getId()); |
| } |
| |
| /** |
| * Releases cached title texture resources for the {@link LayoutTab}. |
| * @param layoutTab The {@link LayoutTab} to release resources for. |
| */ |
| public void releaseResourcesForTab(LayoutTab layoutTab) { |
| mUpdateHost.releaseResourcesForTab(layoutTab.getId()); |
| } |
| |
| /** |
| * Update the animation and give chance to cascade the changes. |
| * @param time The current time of the app in ms. |
| * @param dt The delta time between update frames in ms. |
| * @return Whether the layout is done updating. |
| */ |
| public final boolean onUpdate(long time, long dt) { |
| final boolean doneAnimating = onUpdateAnimation(time, false); |
| |
| // Don't update the layout if onUpdateAnimation ended up making a new layout active. |
| if (mUpdateHost.isActiveLayout(this)) updateLayout(time, dt); |
| |
| return doneAnimating; |
| } |
| |
| /** |
| * Layout-specific updates. Cascades the values updated by the animations. |
| * @param time The current time of the app in ms. |
| * @param dt The delta time between update frames in ms. |
| */ |
| protected void updateLayout(long time, long dt) { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).updateOverlay(time, dt); |
| } |
| } |
| |
| /** |
| * Update snapping to pixel. To be called once every frame. |
| * |
| * TODO(crbug.com/1070281): Temporary placement. This is some Mediator logic and should move to |
| * the appropriate location when doing MVC. |
| * |
| * @param dt The delta time between update frames in ms. |
| * @param layoutTab The {@link LayoutTab} that needs to be updating. |
| * @return True if the snapping requests to render at least one more frame. |
| */ |
| protected boolean updateSnap(long dt, LayoutTab layoutTab) { |
| final float step = dt * SNAP_SPEED / 1000.0f; |
| final float renderX = layoutTab.get(LayoutTab.RENDER_X); |
| final float renderY = layoutTab.get(LayoutTab.RENDER_Y); |
| final float x = updateSnap(step, renderX, layoutTab.get(LayoutTab.X)); |
| final float y = updateSnap(step, renderY, layoutTab.get(LayoutTab.Y)); |
| final boolean change = x != renderX || y != renderY; |
| layoutTab.set(LayoutTab.RENDER_X, x); |
| layoutTab.set(LayoutTab.RENDER_Y, y); |
| return change; |
| } |
| |
| private float updateSnap(float step, float current, float ref) { |
| if (Math.abs(current - ref) > mPxToDp) return ref; |
| final float refRounded = Math.round(ref * mDpToPx) * mPxToDp; |
| if (refRounded < ref) { |
| current -= step; |
| current = Math.max(refRounded, current); |
| } else { |
| current += step; |
| current = Math.min(refRounded, current); |
| } |
| return current; |
| } |
| |
| /** |
| * Request that the renderer render a frame (after the current frame). This |
| * should be called whenever a new frame should be rendered. |
| */ |
| public void requestRender() { |
| mRenderHost.requestRender(); |
| } |
| |
| /** |
| * Requests one more frame of refresh for the transforms and changing properties. Primarily, |
| * this is so animations can continue to animate. |
| */ |
| public void requestUpdate() { |
| mUpdateHost.requestUpdate(); |
| } |
| |
| /** |
| * Called when the context and size of the view has changed. |
| * |
| * @param context The current Android's context. |
| */ |
| public void contextChanged(Context context) { |
| mContext = context; |
| LayoutTab.resetDimensionConstants(context); |
| } |
| |
| /** |
| * Called when the size of the viewport has changed. |
| * @param visibleViewport The visible viewport that represents the area on the screen |
| * this {@link Layout} gets to draw to in px (potentially takes |
| * into account browser controls). |
| * @param screenViewport The viewport of the screen in px. |
| * @param heightMinusBrowserControls The height the {@link Layout} gets excluding the height of |
| * the browser controls in px. TODO(dtrainor): Look at getting rid |
| * of this. |
| * @param orientation The new orientation. Valid values are defined by |
| * {@link Orientation}. |
| */ |
| public final void sizeChanged(RectF visibleViewportPx, RectF screenViewportPx, |
| float topBrowserControlsHeightPx, float bottomBrowserControlsHeightPx, |
| @Orientation int orientation) { |
| // 1. Pull out this Layout's width and height properties based on the viewport. |
| float width = screenViewportPx.width() / mDpToPx; |
| float height = screenViewportPx.height() / mDpToPx; |
| float topBrowserControlsHeightDp = topBrowserControlsHeightPx / mDpToPx; |
| float bottomBrowserControlsHeightDp = bottomBrowserControlsHeightPx / mDpToPx; |
| |
| // 2. Check if any Layout-specific properties have changed. |
| boolean layoutPropertiesChanged = Float.compare(mWidthDp, width) != 0 |
| || Float.compare(mHeightDp, height) != 0 |
| || Float.compare(mTopBrowserControlsHeightDp, topBrowserControlsHeightDp) != 0 |
| || Float.compare(mBottomBrowserControlsHeightDp, bottomBrowserControlsHeightDp) != 0 |
| || mCurrentOrientation != orientation; |
| |
| // 3. Update the internal sizing properties. |
| mWidthDp = width; |
| mHeightDp = height; |
| mTopBrowserControlsHeightDp = topBrowserControlsHeightDp; |
| mBottomBrowserControlsHeightDp = bottomBrowserControlsHeightDp; |
| mCurrentOrientation = orientation; |
| |
| // 4. Notify the actual Layout if necessary. |
| if (layoutPropertiesChanged) { |
| notifySizeChanged(width, height, orientation); |
| } |
| |
| // 5. TODO(dtrainor): Notify the overlay objects. |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).onSizeChanged(width, height, visibleViewportPx.top, orientation); |
| } |
| } |
| |
| /** |
| * Notifies when the size or the orientation of the view has actually changed. |
| * |
| * @param width The new width in dp. |
| * @param height The new height in dp. |
| * @param orientation The new orientation. |
| */ |
| protected void notifySizeChanged(float width, float height, @Orientation int orientation) {} |
| |
| /** |
| * Notify the a title has changed. |
| * |
| * @param tabId The id of the tab that has changed. |
| * @param title The new title. |
| */ |
| public void tabTitleChanged(int tabId, String title) { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).tabTitleChanged(tabId, title); |
| } |
| } |
| |
| /** |
| * Sets the managers needed to for the layout to get information from outside. The managers |
| * are tailored to be called from the GL thread. |
| * |
| * @param modelSelector The {@link TabModelSelector} to be set on the layout. |
| * @param manager The {@link TabContentManager} to get tab display content. |
| */ |
| public void setTabModelSelector(TabModelSelector modelSelector, TabContentManager manager) { |
| if (mTabContentManager != null) mTabContentManager.removeThumbnailChangeListener(this); |
| mTabModelSelector = modelSelector; |
| mTabContentManager = manager; |
| if (mTabContentManager != null) mTabContentManager.addThumbnailChangeListener(this); |
| } |
| |
| /** |
| * @return The sizing mode for the layout. |
| */ |
| public @ViewportMode int getViewportMode() { |
| return ViewportMode.ALWAYS_SHOWING_BROWSER_CONTROLS; |
| } |
| |
| /** |
| * Informs this cache of the visible {@link Tab} {@code id}s, as well as the |
| * primary screen-filling tab. |
| */ |
| protected void updateCacheVisibleIdsAndPrimary(List<Integer> visible, int primaryTabId) { |
| if (mTabContentManager != null) mTabContentManager.updateVisibleIds(visible, primaryTabId); |
| } |
| |
| /** |
| * Informs this cache of the visible {@link Tab} {@code id}s, in cases where there |
| * is no primary screen-filling tab. |
| */ |
| protected void updateCacheVisibleIds(List<Integer> visible) { |
| if (mTabContentManager != null) mTabContentManager.updateVisibleIds(visible, -1); |
| } |
| |
| /** |
| * To be called when the layout is starting a transition out of the view mode. |
| * @param nextTabId The id of the next tab. |
| * @param hintAtTabSelection Whether or not the new tab selection should be broadcast as a hint |
| * potentially before this {@link Layout} is done hiding and the |
| * selection occurs. |
| */ |
| public void startHiding(int nextTabId, boolean hintAtTabSelection) { |
| mUpdateHost.startHiding(nextTabId, hintAtTabSelection); |
| mIsHiding = true; |
| mNextTabId = nextTabId; |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).onHideLayout(); |
| } |
| } |
| |
| /** |
| * @return True is the layout is in the process of hiding itself. |
| */ |
| public boolean isHiding() { |
| return mIsHiding; |
| } |
| |
| /** |
| * @return The incognito state of the layout. |
| */ |
| public boolean isIncognito() { |
| return mTabModelSelector.isIncognitoSelected(); |
| } |
| |
| /** |
| * To be called when the transition into the layout is done. |
| */ |
| public void doneShowing() { |
| mUpdateHost.doneShowing(); |
| } |
| |
| /** |
| * To be called when the transition out of the view mode is done. |
| * This is currently called by the renderer when all the animation are done while hiding. |
| */ |
| public void doneHiding() { |
| mIsHiding = false; |
| if (mNextTabId != Tab.INVALID_TAB_ID) { |
| TabModel model = mTabModelSelector.getModelForTabId(mNextTabId); |
| if (model != null) { |
| TabModelUtils.setIndex(model, TabModelUtils.getTabIndexById(model, mNextTabId)); |
| } |
| mNextTabId = Tab.INVALID_TAB_ID; |
| } |
| mUpdateHost.doneHiding(); |
| if (mRenderHost != null && mRenderHost.getResourceManager() != null) { |
| mRenderHost.getResourceManager().clearTintedResourceCache(); |
| } |
| } |
| |
| /** |
| * Called when a tab is getting selected. Typically when exiting the overview mode. |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the selected tab. |
| */ |
| public void onTabSelecting(long time, int tabId) { |
| startHiding(tabId, true); |
| } |
| |
| /** |
| * Initialize the layout to be shown. |
| * @param time The current time of the app in ms. |
| * @param animate Whether to play an entry animation. |
| */ |
| public void show(long time, boolean animate) { |
| mIsHiding = false; |
| mNextTabId = Tab.INVALID_TAB_ID; |
| } |
| |
| /** |
| * Hands the layout an Android view to attach it's views to. |
| * @param container The Android View to attach the layout's views to. |
| */ |
| public void attachViews(ViewGroup container) { } |
| |
| /** |
| * Signal to the Layout to detach it's views from the container. |
| */ |
| public void detachViews() { } |
| |
| /** |
| * Forces the current animation to finish and broadcasts the proper event. |
| */ |
| protected void forceAnimationToFinish() {} |
| |
| /** |
| * @return The width of the drawing area in dp. |
| */ |
| public float getWidth() { |
| return mWidthDp; |
| } |
| |
| /** |
| * @return The height of the drawing area in dp. |
| */ |
| public float getHeight() { |
| return mHeightDp; |
| } |
| |
| /** |
| * @return The height of the top browser controls in dp. |
| */ |
| public float getTopBrowserControlsHeight() { |
| return mTopBrowserControlsHeightDp; |
| } |
| |
| /** |
| * @return The height of the bottom browser controls in dp. |
| */ |
| public float getBottomBrowserControlsHeight() { |
| return mBottomBrowserControlsHeightDp; |
| } |
| |
| /** |
| * @return The height of the drawing area minus the browser controls in dp. |
| */ |
| public float getHeightMinusBrowserControls() { |
| return getHeight() - (getTopBrowserControlsHeight() + getBottomBrowserControlsHeight()); |
| } |
| |
| /** |
| * @see Orientation |
| * @return The orientation of the screen (portrait or landscape). Values are defined by |
| * {@link Orientation}. |
| */ |
| public @Orientation int getOrientation() { |
| return mCurrentOrientation; |
| } |
| |
| /** |
| * Initializes a {@link LayoutTab} with data from the {@link LayoutUpdateHost}. This function |
| * eventually needs to be called but may be overridden to manage the posting traffic. |
| * |
| * @param layoutTab The {@link LayoutTab} To initialize from a |
| * {@link Tab} on the UI thread. |
| * @return Whether the asynchronous initialization of the {@link LayoutTab} has really |
| * been posted. |
| */ |
| protected boolean initLayoutTabFromHost(LayoutTab layoutTab) { |
| if (layoutTab.isInitFromHostNeeded()) { |
| mUpdateHost.initLayoutTabFromHost(layoutTab.getId()); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Called by the LayoutManager when an animation should be killed. |
| */ |
| public void unstallImmediately() { } |
| |
| /** |
| * Called by the LayoutManager when an animation should be killed. |
| * @param tabId The tab that the kill signal is associated with |
| */ |
| public void unstallImmediately(int tabId) { } |
| |
| /** |
| * Called by the LayoutManager when they system back button is pressed. |
| * @return Whether or not the layout consumed the event. |
| */ |
| public boolean onBackPressed() { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| // If the back button was consumed by any overlays, return true. |
| if (mSceneOverlays.get(i).onBackPressed()) return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Called when a tab get selected. Typically when a tab get closed and the new current tab get |
| * selected. |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the selected tab. |
| * @param prevId The id of the previously selected tab. |
| * @param incognito Whether or not the affected model was incognito. |
| */ |
| public void onTabSelected(long time, int tabId, int prevId, boolean incognito) { |
| } |
| |
| /** |
| * Called when a tab is about to be closed. When called, the closing tab will still |
| * be part of the model. |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the tab being closed |
| */ |
| public void onTabClosing(long time, int tabId) { |
| } |
| |
| /** |
| * Called when a tab is being closed. When called, the closing tab will not |
| * be part of the model. |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the tab being closed. |
| * @param nextTabId The id if the tab that is being switched to. |
| * @param incognito Whether or not the affected model was incognito. |
| */ |
| public void onTabClosed(long time, int tabId, int nextTabId, boolean incognito) { |
| } |
| |
| /** |
| * Called when all the tabs in the current stack need to be closed. |
| * When called, the tabs will still be part of the model. |
| * @param time The current time of the app in ms. |
| * @param incognito True if this the incognito tab model should close all tabs, false otherwise. |
| */ |
| public void onTabsAllClosing(long time, boolean incognito) { |
| } |
| |
| /** |
| * Called before a tab is created from the top left button. |
| * |
| * @param sourceTabId The id of the source tab. |
| */ |
| public void onTabCreating(int sourceTabId) { } |
| |
| /** |
| * Called when a tab is created from the top left button. |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the newly created tab. |
| * @param tabIndex The index of the newly created tab. |
| * @param sourceTabId The id of the source tab. |
| * @param newIsIncognito Whether the new tab is incognito. |
| * @param background Whether the tab is created in the background. |
| * @param originX The X screen coordinate in dp of the last touch down event that spawned |
| * this tab. |
| * @param originY The Y screen coordinate in dp of the last touch down event that spawned |
| * this tab. |
| */ |
| public void onTabCreated(long time, int tabId, int tabIndex, int sourceTabId, |
| boolean newIsIncognito, boolean background, float originX, float originY) { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).tabCreated(time, newIsIncognito, tabId, sourceTabId, !background); |
| } |
| } |
| |
| /** |
| * Called when a tab is restored (created FROM_RESTORE). |
| * @param time The current time of the app in ms. |
| * @param tabId The id of the restored tab. |
| */ |
| public void onTabRestored(long time, int tabId) { } |
| |
| /** |
| * Called when the TabModelSelector has been initialized with an accurate tab count. |
| */ |
| public void onTabStateInitialized() { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).tabStateInitialized(); |
| } |
| } |
| |
| /** |
| * Called when the current tabModel switched (e.g. standard -> incognito). |
| * |
| * @param incognito True if the new model is incognito. |
| */ |
| public void onTabModelSwitched(boolean incognito) { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| mSceneOverlays.get(i).tabModelSwitched(incognito); |
| } |
| } |
| |
| /** |
| * Called when a tab is finally closed if the action was previously undoable. |
| * @param time The current time of the app in ms. |
| * @param id The id of the Tab. |
| * @param incognito True if the tab is incognito |
| */ |
| public void onTabClosureCommitted(long time, int id, boolean incognito) { } |
| |
| @Override |
| public void onThumbnailChange(int id) { |
| requestUpdate(); |
| } |
| |
| /** |
| * Steps the animation forward and updates all the animated values. |
| * @param time The current time of the app in ms. |
| * @param jumpToEnd Whether to finish the animation. |
| * @return Whether the animation was finished. |
| */ |
| protected boolean onUpdateAnimation(long time, boolean jumpToEnd) { |
| return true; |
| } |
| |
| /** |
| * @return Whether or not there is an animation currently being driven by this {@link Layout}. |
| */ |
| @VisibleForTesting |
| public boolean isLayoutAnimating() { |
| return false; |
| } |
| |
| /** |
| * @return The {@link LayoutTab}s to be drawn. |
| */ |
| public LayoutTab[] getLayoutTabsToRender() { |
| return mLayoutTabs; |
| } |
| |
| /** |
| * @param id The id of the {@link LayoutTab} to search for. |
| * @return A {@link LayoutTab} represented by a {@link Tab} with an id of {@code id}. |
| */ |
| public LayoutTab getLayoutTab(int id) { |
| if (mLayoutTabs != null) { |
| for (int i = 0; i < mLayoutTabs.length; i++) { |
| if (mLayoutTabs[i].getId() == id) return mLayoutTabs[i]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return Whether the layout is handling the model updates when a tab is closing. |
| */ |
| public boolean handlesTabClosing() { |
| return false; |
| } |
| |
| /** |
| * @return Whether the layout is handling the model updates when a tab is creating. |
| */ |
| public boolean handlesTabCreating() { |
| if (mLayoutTabs == null || mLayoutTabs.length != 1) return false; |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| if (mSceneOverlays.get(i).handlesTabCreating()) { |
| // Prevent animation from happening if the overlay handles creation. |
| startHiding(mLayoutTabs[0].getId(), false); |
| doneHiding(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return Whether the layout is handling the model updates when closing all the tabs. |
| */ |
| public boolean handlesCloseAll() { |
| return false; |
| } |
| |
| /** |
| * Whether or not the toolbar IncognitoToggleButton (if present) should be enabled. E.g., it can |
| * be disabled while animating a tab selection to avoid odd behavior. |
| */ |
| public boolean shouldAllowIncognitoSwitching() { |
| return true; |
| } |
| |
| /** |
| * @return True if the content decoration layer should be shown. |
| */ |
| public boolean shouldDisplayContentOverlay() { |
| return false; |
| } |
| |
| /** |
| * @return True if the host container can set itself as focusable e.g. for accessibility. |
| * Subclasses can override e.g. to provide a different default focused view. |
| */ |
| public boolean canHostBeFocusable() { |
| return true; |
| } |
| |
| /** |
| * @param e The {@link MotionEvent} to consider. |
| * @param offsets The current touch offsets that should be applied to the |
| * {@link EventFilter}s. |
| * @param isKeyboardShowing Whether or not the keyboard is showing. |
| * @return The {@link EventFilter} the {@link Layout} is listening to. |
| */ |
| public EventFilter findInterceptingEventFilter( |
| MotionEvent e, PointF offsets, boolean isKeyboardShowing) { |
| // The last added overlay will be drawn on top of everything else, therefore the last |
| // filter added should have the first chance to intercept any touch events. |
| for (int i = mSceneOverlays.size() - 1; i >= 0; i--) { |
| EventFilter eventFilter = mSceneOverlays.get(i).getEventFilter(); |
| if (eventFilter == null) continue; |
| if (offsets != null) eventFilter.setCurrentMotionEventOffsets(offsets.x, offsets.y); |
| if (eventFilter.onInterceptTouchEvent(e, isKeyboardShowing)) return eventFilter; |
| } |
| |
| EventFilter layoutEventFilter = getEventFilter(); |
| if (layoutEventFilter != null) { |
| if (offsets != null) { |
| layoutEventFilter.setCurrentMotionEventOffsets(offsets.x, offsets.y); |
| } |
| if (layoutEventFilter.onInterceptTouchEvent(e, isKeyboardShowing)) { |
| return layoutEventFilter; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Build a {@link SceneLayer} if it hasn't already been built, and update it and return it. |
| * |
| * @param viewport A viewport in which to display content in px. |
| * @param visibleViewport The visible section of the viewport in px. |
| * @param layerTitleCache A layer title cache. |
| * @param tabContentManager A tab content manager. |
| * @param resourceManager A resource manager. |
| * @param browserControls A browser controls state provider. |
| * @return A {@link SceneLayer} that represents the content for this |
| * {@link Layout}. |
| */ |
| public final SceneLayer getUpdatedSceneLayer(RectF viewport, RectF visibleViewport, |
| LayerTitleCache layerTitleCache, TabContentManager tabContentManager, |
| ResourceManager resourceManager, BrowserControlsStateProvider browserControls) { |
| updateSceneLayer(viewport, visibleViewport, layerTitleCache, tabContentManager, |
| resourceManager, browserControls); |
| |
| float offsetPx = browserControls != null ? browserControls.getTopControlOffset() : 0.f; |
| float dpToPx = getContext().getResources().getDisplayMetrics().density; |
| float offsetDp = offsetPx / dpToPx; |
| |
| SceneLayer content = getSceneLayer(); |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| // If the SceneOverlay is not showing, don't bother adding it to the tree. |
| if (!mSceneOverlays.get(i).isSceneOverlayTreeShowing()) continue; |
| |
| SceneOverlayLayer overlayLayer = mSceneOverlays.get(i).getUpdatedSceneOverlayTree( |
| viewport, visibleViewport, layerTitleCache, resourceManager, offsetDp); |
| |
| overlayLayer.setContentTree(content); |
| content = overlayLayer; |
| } |
| |
| return content; |
| } |
| |
| /** |
| * @return Whether or not to force the browser controls Android view to hide. |
| */ |
| public boolean forceHideBrowserControlsAndroidView() { |
| for (int i = 0; i < mSceneOverlays.size(); i++) { |
| // If any overlay wants to hide tha Android version of the browser controls, hide them. |
| if (mSceneOverlays.get(i).shouldHideAndroidBrowserControls()) return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @return Whether or not the layout should permanently show the browser controls. |
| */ |
| public boolean forceShowBrowserControlsAndroidView() { |
| return false; |
| } |
| |
| /** |
| * @return The EventFilter to use for processing events for this Layout. |
| */ |
| protected abstract EventFilter getEventFilter(); |
| |
| /** |
| * Get an instance of {@link SceneLayer}. Any class inheriting {@link Layout} |
| * should override this function in order for other functions to work. |
| * |
| * @return The scene layer for this {@link Layout}. |
| */ |
| protected abstract SceneLayer getSceneLayer(); |
| |
| /** |
| * Update {@link SceneLayer} instance this layout holds. Any class inheriting {@link Layout} |
| * should override this function in order for other functions to work. |
| */ |
| protected void updateSceneLayer(RectF viewport, RectF contentViewport, |
| LayerTitleCache layerTitleCache, TabContentManager tabContentManager, |
| ResourceManager resourceManager, BrowserControlsStateProvider browserControls) {} |
| } |