| // 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.overlays.strip; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.graphics.RectF; |
| import android.os.SystemClock; |
| |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.ChromeFeatureList; |
| import org.chromium.chrome.browser.compositor.LayerTitleCache; |
| import org.chromium.chrome.browser.compositor.layouts.LayoutManager; |
| import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost; |
| import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost; |
| import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton; |
| import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton.CompositorOnClickHandler; |
| import org.chromium.chrome.browser.compositor.layouts.components.VirtualView; |
| import org.chromium.chrome.browser.compositor.layouts.eventfilter.AreaGestureEventFilter; |
| import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter; |
| import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureHandler; |
| import org.chromium.chrome.browser.compositor.overlays.SceneOverlay; |
| import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer; |
| import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayer; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tabmodel.TabCreatorManager; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; |
| import org.chromium.ui.base.LocalizationUtils; |
| import org.chromium.ui.resources.ResourceManager; |
| |
| import java.util.List; |
| |
| /** |
| * This class handles managing which {@link StripLayoutHelper} is currently active and dispatches |
| * all input and model events to the proper destination. |
| */ |
| public class StripLayoutHelperManager implements SceneOverlay { |
| // Caching Variables |
| private final RectF mStripFilterArea = new RectF(); |
| |
| // Model selector buttons constants. |
| private static final float MODEL_SELECTOR_BUTTON_Y_OFFSET_DP = 10.f; |
| private static final float MODEL_SELECTOR_BUTTON_END_PADDING_DP = 6.f; |
| private static final float MODEL_SELECTOR_BUTTON_START_PADDING_DP = 3.f; |
| private static final float MODEL_SELECTOR_BUTTON_WIDTH_DP = 24.f; |
| private static final float MODEL_SELECTOR_BUTTON_HEIGHT_DP = 24.f; |
| |
| // External influences |
| private TabModelSelector mTabModelSelector; |
| private final LayoutUpdateHost mUpdateHost; |
| |
| // Event Filters |
| private final AreaGestureEventFilter mEventFilter; |
| |
| // Internal state |
| private boolean mIsIncognito; |
| private final StripLayoutHelper mNormalHelper; |
| private final StripLayoutHelper mIncognitoHelper; |
| |
| // UI State |
| private float mWidth; // in dp units |
| private final float mHeight; // in dp units |
| private int mOrientation; |
| private final CompositorButton mModelSelectorButton; |
| |
| private TabStripSceneLayer mTabStripTreeProvider; |
| |
| private TabStripEventHandler mTabStripEventHandler; |
| |
| private class TabStripEventHandler implements GestureHandler { |
| @Override |
| public void onDown(float x, float y, boolean fromMouse, int buttons) { |
| if (mModelSelectorButton.onDown(x, y)) return; |
| getActiveStripLayoutHelper().onDown(time(), x, y, fromMouse, buttons); |
| } |
| |
| @Override |
| public void onUpOrCancel() { |
| if (mModelSelectorButton.onUpOrCancel() && mTabModelSelector != null) { |
| getActiveStripLayoutHelper().finishAnimation(); |
| if (!mModelSelectorButton.isVisible()) return; |
| mTabModelSelector.selectModel(!mTabModelSelector.isIncognitoSelected()); |
| return; |
| } |
| getActiveStripLayoutHelper().onUpOrCancel(time()); |
| } |
| |
| @Override |
| public void drag(float x, float y, float dx, float dy, float tx, float ty) { |
| mModelSelectorButton.drag(x, y); |
| getActiveStripLayoutHelper().drag(time(), x, y, dx, dy, tx, ty); |
| } |
| |
| @Override |
| public void click(float x, float y, boolean fromMouse, int buttons) { |
| long time = time(); |
| if (mModelSelectorButton.click(x, y)) { |
| mModelSelectorButton.handleClick(time); |
| return; |
| } |
| getActiveStripLayoutHelper().click(time(), x, y, fromMouse, buttons); |
| } |
| |
| @Override |
| public void fling(float x, float y, float velocityX, float velocityY) { |
| getActiveStripLayoutHelper().fling(time(), x, y, velocityX, velocityY); |
| } |
| |
| @Override |
| public void onLongPress(float x, float y) { |
| getActiveStripLayoutHelper().onLongPress(time(), x, y); |
| } |
| |
| @Override |
| public void onPinch(float x0, float y0, float x1, float y1, boolean firstEvent) { |
| // Not implemented. |
| } |
| |
| private long time() { |
| return LayoutManager.time(); |
| } |
| } |
| |
| /** |
| * Creates an instance of the {@link StripLayoutHelperManager}. |
| * @param context The current Android {@link Context}. |
| * @param updateHost The parent {@link LayoutUpdateHost}. |
| * @param renderHost The {@link LayoutRenderHost}. |
| */ |
| public StripLayoutHelperManager( |
| Context context, LayoutUpdateHost updateHost, LayoutRenderHost renderHost) { |
| mUpdateHost = updateHost; |
| mTabStripTreeProvider = new TabStripSceneLayer(context); |
| mTabStripEventHandler = new TabStripEventHandler(); |
| mEventFilter = |
| new AreaGestureEventFilter(context, mTabStripEventHandler, null, false, false); |
| |
| mNormalHelper = new StripLayoutHelper(context, updateHost, renderHost, false); |
| mIncognitoHelper = new StripLayoutHelper(context, updateHost, renderHost, true); |
| |
| CompositorOnClickHandler selectorClickHandler = new CompositorOnClickHandler() { |
| @Override |
| public void onClick(long time) { |
| handleModelSelectorButtonClick(); |
| } |
| }; |
| mModelSelectorButton = new CompositorButton(context, MODEL_SELECTOR_BUTTON_WIDTH_DP, |
| MODEL_SELECTOR_BUTTON_HEIGHT_DP, selectorClickHandler); |
| mModelSelectorButton.setIncognito(false); |
| mModelSelectorButton.setVisible(false); |
| // Pressed resources are the same as the unpressed resources. |
| mModelSelectorButton.setResources(R.drawable.btn_tabstrip_switch_normal, |
| R.drawable.btn_tabstrip_switch_normal, R.drawable.btn_tabstrip_switch_incognito, |
| R.drawable.btn_tabstrip_switch_incognito); |
| mModelSelectorButton.setY(MODEL_SELECTOR_BUTTON_Y_OFFSET_DP); |
| |
| Resources res = context.getResources(); |
| mHeight = res.getDimension(R.dimen.tab_strip_height) / res.getDisplayMetrics().density; |
| boolean useAlternativeIncognitoStrings = |
| ChromeFeatureList.isEnabled(ChromeFeatureList.INCOGNITO_STRINGS); |
| mModelSelectorButton.setAccessibilityDescription( |
| res.getString(useAlternativeIncognitoStrings |
| ? R.string.accessibility_tabstrip_btn_private_toggle_standard |
| : R.string.accessibility_tabstrip_btn_incognito_toggle_standard), |
| res.getString(useAlternativeIncognitoStrings |
| ? R.string.accessibility_tabstrip_btn_private_toggle_private |
| : R.string.accessibility_tabstrip_btn_incognito_toggle_incognito)); |
| |
| onContextChanged(context); |
| } |
| |
| /** |
| * Cleans up internal state. |
| */ |
| public void destroy() { |
| mTabStripTreeProvider.destroy(); |
| mTabStripTreeProvider = null; |
| mIncognitoHelper.destroy(); |
| mNormalHelper.destroy(); |
| } |
| |
| private void handleModelSelectorButtonClick() { |
| if (mTabModelSelector == null) return; |
| getActiveStripLayoutHelper().finishAnimation(); |
| if (!mModelSelectorButton.isVisible()) return; |
| mTabModelSelector.selectModel(!mTabModelSelector.isIncognitoSelected()); |
| } |
| |
| @VisibleForTesting |
| public void simulateClick(float x, float y, boolean fromMouse, int buttons) { |
| mTabStripEventHandler.click(x, y, fromMouse, buttons); |
| } |
| |
| @VisibleForTesting |
| public void simulateLongPress(float x, float y) { |
| mTabStripEventHandler.onLongPress(x, y); |
| } |
| |
| @Override |
| public SceneOverlayLayer getUpdatedSceneOverlayTree(RectF viewport, RectF visibleViewport, |
| LayerTitleCache layerTitleCache, ResourceManager resourceManager, float yOffset) { |
| assert mTabStripTreeProvider != null; |
| |
| Tab selectedTab = mTabModelSelector.getCurrentModel().getTabAt( |
| mTabModelSelector.getCurrentModel().index()); |
| int selectedTabId = selectedTab == null ? TabModel.INVALID_TAB_INDEX : selectedTab.getId(); |
| mTabStripTreeProvider.pushAndUpdateStrip(this, layerTitleCache, resourceManager, |
| getActiveStripLayoutHelper().getStripLayoutTabsToRender(), yOffset, |
| selectedTabId); |
| return mTabStripTreeProvider; |
| } |
| |
| @Override |
| public boolean isSceneOverlayTreeShowing() { |
| // TODO(mdjones): This matches existing behavior but can be improved to return false if |
| // the browser controls offset is equal to the browser controls height. |
| return true; |
| } |
| |
| @Override |
| public EventFilter getEventFilter() { |
| return mEventFilter; |
| } |
| |
| @Override |
| public void onSizeChanged( |
| float width, float height, float visibleViewportOffsetY, int orientation) { |
| mWidth = width; |
| mOrientation = orientation; |
| if (!LocalizationUtils.isLayoutRtl()) { |
| mModelSelectorButton.setX( |
| mWidth - MODEL_SELECTOR_BUTTON_WIDTH_DP - MODEL_SELECTOR_BUTTON_END_PADDING_DP); |
| } else { |
| mModelSelectorButton.setX(MODEL_SELECTOR_BUTTON_END_PADDING_DP); |
| } |
| |
| mNormalHelper.onSizeChanged(mWidth, mHeight); |
| mIncognitoHelper.onSizeChanged(mWidth, mHeight); |
| |
| mStripFilterArea.set(0, 0, mWidth, Math.min(getHeight(), visibleViewportOffsetY)); |
| mEventFilter.setEventArea(mStripFilterArea); |
| } |
| |
| public CompositorButton getNewTabButton() { |
| return getActiveStripLayoutHelper().getNewTabButton(); |
| } |
| |
| public CompositorButton getModelSelectorButton() { |
| return mModelSelectorButton; |
| } |
| |
| @Override |
| public void getVirtualViews(List<VirtualView> views) { |
| if (mModelSelectorButton.isVisible()) views.add(mModelSelectorButton); |
| getActiveStripLayoutHelper().getVirtualViews(views); |
| } |
| |
| @Override |
| public boolean shouldHideAndroidBrowserControls() { |
| return false; |
| } |
| |
| /** |
| * @return The opacity to use for the fade on the left side of the tab strip. |
| */ |
| public float getLeftFadeOpacity() { |
| return getActiveStripLayoutHelper().getLeftFadeOpacity(); |
| } |
| |
| /** |
| * @return The opacity to use for the fade on the right side of the tab strip. |
| */ |
| public float getRightFadeOpacity() { |
| return getActiveStripLayoutHelper().getRightFadeOpacity(); |
| } |
| |
| /** |
| * @return The brightness of background tabs in the tabstrip. |
| */ |
| public float getBackgroundTabBrightness() { |
| return getActiveStripLayoutHelper().getBackgroundTabBrightness(); |
| } |
| |
| /** |
| * @return The brightness of the entire tabstrip. |
| */ |
| public float getBrightness() { |
| return getActiveStripLayoutHelper().getBrightness(); |
| } |
| |
| /** |
| * Sets the {@link TabModelSelector} that this {@link StripLayoutHelperManager} will visually |
| * represent, and various objects associated with it. |
| * @param modelSelector The {@link TabModelSelector} to visually represent. |
| * @param tabCreatorManager The {@link TabCreatorManager}, used to create new tabs. |
| */ |
| public void setTabModelSelector(TabModelSelector modelSelector, |
| TabCreatorManager tabCreatorManager) { |
| if (mTabModelSelector == modelSelector) return; |
| |
| mTabModelSelector = modelSelector; |
| mNormalHelper.setTabModel(mTabModelSelector.getModel(false), |
| tabCreatorManager.getTabCreator(false)); |
| mIncognitoHelper.setTabModel(mTabModelSelector.getModel(true), |
| tabCreatorManager.getTabCreator(true)); |
| tabModelSwitched(mTabModelSelector.isIncognitoSelected()); |
| |
| new TabModelSelectorTabModelObserver(modelSelector) { |
| /** |
| * @return The actual current time of the app in ms. |
| */ |
| public long time() { |
| return SystemClock.uptimeMillis(); |
| } |
| |
| @Override |
| public void tabRemoved(Tab tab) { |
| getStripLayoutHelper(tab.isIncognito()).tabClosed(time(), tab.getId()); |
| updateModelSwitcherButton(); |
| } |
| |
| @Override |
| public void didMoveTab(Tab tab, int newIndex, int curIndex) { |
| getStripLayoutHelper(tab.isIncognito()) |
| .tabMoved(time(), tab.getId(), curIndex, newIndex); |
| } |
| |
| @Override |
| public void tabClosureUndone(Tab tab) { |
| getStripLayoutHelper(tab.isIncognito()).tabClosureCancelled(time(), tab.getId()); |
| updateModelSwitcherButton(); |
| } |
| |
| @Override |
| public void tabPendingClosure(Tab tab) { |
| getStripLayoutHelper(tab.isIncognito()).tabClosed(time(), tab.getId()); |
| updateModelSwitcherButton(); |
| } |
| |
| @Override |
| public void didCloseTab(int tabId, boolean incognito) { |
| getStripLayoutHelper(incognito).tabClosed(time(), tabId); |
| updateModelSwitcherButton(); |
| } |
| |
| @Override |
| public void didSelectTab(Tab tab, @TabModel.TabSelectionType int type, int lastId) { |
| if (tab.getId() == lastId) return; |
| getStripLayoutHelper(tab.isIncognito()).tabSelected(time(), tab.getId(), lastId); |
| } |
| }; |
| |
| new TabModelSelectorTabObserver(modelSelector) { |
| @Override |
| public void onLoadStarted(Tab tab, boolean toDifferentDocument) { |
| getStripLayoutHelper(tab.isIncognito()).tabLoadStarted(tab.getId()); |
| } |
| |
| @Override |
| public void onLoadStopped(Tab tab, boolean toDifferentDocument) { |
| getStripLayoutHelper(tab.isIncognito()).tabLoadFinished(tab.getId()); |
| } |
| |
| @Override |
| public void onPageLoadStarted(Tab tab, String url) { |
| getStripLayoutHelper(tab.isIncognito()).tabPageLoadStarted(tab.getId()); |
| } |
| |
| @Override |
| public void onPageLoadFinished(Tab tab) { |
| getStripLayoutHelper(tab.isIncognito()).tabPageLoadFinished(tab.getId()); |
| } |
| |
| @Override |
| public void onPageLoadFailed(Tab tab, int errorCode) { |
| getStripLayoutHelper(tab.isIncognito()).tabPageLoadFinished(tab.getId()); |
| } |
| |
| @Override |
| public void onCrash(Tab tab, boolean sadTabShown) { |
| getStripLayoutHelper(tab.isIncognito()).tabPageLoadFinished(tab.getId()); |
| } |
| }; |
| } |
| |
| @Override |
| public void tabTitleChanged(int tabId, String title) { |
| getActiveStripLayoutHelper().tabTitleChanged(tabId, title); |
| } |
| |
| public float getHeight() { |
| return mHeight; |
| } |
| |
| public float getWidth() { |
| return mWidth; |
| } |
| |
| public int getOrientation() { |
| return mOrientation; |
| } |
| |
| /** |
| * Updates all internal resources and dimensions. |
| * @param context The current Android {@link Context}. |
| */ |
| public void onContextChanged(Context context) { |
| mNormalHelper.onContextChanged(context); |
| mIncognitoHelper.onContextChanged(context); |
| } |
| |
| @Override |
| public boolean updateOverlay(long time, long dt) { |
| getInactiveStripLayoutHelper().finishAnimation(); |
| return getActiveStripLayoutHelper().updateLayout(time, dt); |
| } |
| |
| @Override |
| public boolean onBackPressed() { |
| return false; |
| } |
| |
| @Override |
| public void onHideLayout() {} |
| |
| @Override |
| public boolean handlesTabCreating() { |
| return false; |
| } |
| |
| @Override |
| public void tabStateInitialized() { |
| updateModelSwitcherButton(); |
| } |
| |
| @Override |
| public void tabModelSwitched(boolean incognito) { |
| if (incognito == mIsIncognito) return; |
| mIsIncognito = incognito; |
| |
| mIncognitoHelper.tabModelSelected(mIsIncognito); |
| mNormalHelper.tabModelSelected(!mIsIncognito); |
| |
| updateModelSwitcherButton(); |
| |
| mUpdateHost.requestUpdate(); |
| } |
| |
| private void updateModelSwitcherButton() { |
| mModelSelectorButton.setIncognito(mIsIncognito); |
| if (mTabModelSelector != null) { |
| boolean isVisible = mTabModelSelector.getModel(true).getCount() != 0; |
| mModelSelectorButton.setVisible(isVisible); |
| |
| float endMargin = isVisible |
| ? MODEL_SELECTOR_BUTTON_WIDTH_DP + MODEL_SELECTOR_BUTTON_END_PADDING_DP |
| + MODEL_SELECTOR_BUTTON_START_PADDING_DP |
| : 0.0f; |
| |
| mNormalHelper.setEndMargin(endMargin); |
| mIncognitoHelper.setEndMargin(endMargin); |
| } |
| } |
| |
| @Override |
| public void tabCreated(long time, boolean incognito, int id, int prevId, boolean selected) { |
| getStripLayoutHelper(incognito).tabCreated(time, id, prevId, selected); |
| } |
| |
| /** |
| * @param incognito Whether or not you want the incognito StripLayoutHelper |
| * @return The requested StripLayoutHelper. |
| */ |
| @VisibleForTesting |
| public StripLayoutHelper getStripLayoutHelper(boolean incognito) { |
| return incognito ? mIncognitoHelper : mNormalHelper; |
| } |
| |
| /** |
| * @return The currently visible strip layout helper. |
| */ |
| @VisibleForTesting |
| public StripLayoutHelper getActiveStripLayoutHelper() { |
| return getStripLayoutHelper(mIsIncognito); |
| } |
| |
| private StripLayoutHelper getInactiveStripLayoutHelper() { |
| return mIsIncognito ? mNormalHelper : mIncognitoHelper; |
| } |
| } |