| // 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.toolbar; |
| |
| import android.content.Context; |
| import android.graphics.Canvas; |
| import android.graphics.PorterDuff; |
| import android.graphics.Rect; |
| import android.graphics.Region; |
| import android.util.AttributeSet; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewStub; |
| import android.widget.FrameLayout; |
| |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandler; |
| import org.chromium.chrome.browser.compositor.resources.ResourceFactory; |
| import org.chromium.chrome.browser.contextualsearch.SwipeRecognizer; |
| import org.chromium.chrome.browser.util.ViewUtils; |
| import org.chromium.chrome.browser.widget.ClipDrawableProgressBar.DrawingInfo; |
| import org.chromium.chrome.browser.widget.ControlContainer; |
| import org.chromium.chrome.browser.widget.ToolbarProgressBar; |
| import org.chromium.chrome.browser.widget.ViewResourceFrameLayout; |
| import org.chromium.ui.UiUtils; |
| import org.chromium.ui.resources.dynamics.ViewResourceAdapter; |
| |
| /** |
| * Layout for the browser controls (omnibox, menu, tab strip, etc..). |
| */ |
| public class ToolbarControlContainer extends FrameLayout implements ControlContainer { |
| private final float mTabStripHeight; |
| |
| private Toolbar mToolbar; |
| private ToolbarViewResourceFrameLayout mToolbarContainer; |
| private View mMenuBtn; |
| |
| private final SwipeRecognizer mSwipeRecognizer; |
| private EdgeSwipeHandler mSwipeHandler; |
| |
| /** |
| * Constructs a new control container. |
| * <p> |
| * This constructor is used when inflating from XML. |
| * |
| * @param context The context used to build this view. |
| * @param attrs The attributes used to determine how to construct this view. |
| */ |
| public ToolbarControlContainer(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| mTabStripHeight = context.getResources().getDimension(R.dimen.tab_strip_height); |
| mSwipeRecognizer = new SwipeRecognizerImpl(context); |
| } |
| |
| @Override |
| public ViewResourceAdapter getToolbarResourceAdapter() { |
| return mToolbarContainer.getResourceAdapter(); |
| } |
| |
| @Override |
| public View getView() { |
| return this; |
| } |
| |
| @Override |
| public void getProgressBarDrawingInfo(DrawingInfo drawingInfoOut) { |
| // TODO(yusufo): Avoid casting to the layout without making the interface bigger. |
| ToolbarProgressBar progressBar = ((ToolbarLayout) mToolbar).getProgressBar(); |
| if (progressBar != null) progressBar.getDrawingInfo(drawingInfoOut); |
| } |
| |
| @Override |
| public int getToolbarBackgroundColor() { |
| return ((ToolbarLayout) mToolbar).getToolbarDataProvider().getPrimaryColor(); |
| } |
| |
| @Override |
| public void setSwipeHandler(EdgeSwipeHandler handler) { |
| mSwipeHandler = handler; |
| mSwipeRecognizer.setSwipeHandler(handler); |
| } |
| |
| @Override |
| public void initWithToolbar(int toolbarLayoutId) { |
| ViewStub toolbarStub = (ViewStub) findViewById(R.id.toolbar_stub); |
| toolbarStub.setLayoutResource(toolbarLayoutId); |
| toolbarStub.inflate(); |
| |
| mToolbar = (Toolbar) findViewById(R.id.toolbar); |
| mToolbarContainer = (ToolbarViewResourceFrameLayout) findViewById(R.id.toolbar_container); |
| mToolbarContainer.setToolbar(mToolbar); |
| mMenuBtn = findViewById(R.id.menu_button); |
| |
| if (mToolbar instanceof ToolbarTablet) { |
| // On tablet, draw a fake tab strip and toolbar until the compositor is ready to draw |
| // the real tab strip. (On phone, the toolbar is made entirely of Android views, which |
| // are already initialized.) |
| setBackgroundResource(R.drawable.toolbar_background); |
| } |
| |
| assert mToolbar != null; |
| assert mMenuBtn != null; |
| } |
| |
| @Override |
| public boolean gatherTransparentRegion(Region region) { |
| // Reset the translation on the control container before attempting to compute the |
| // transparent region. |
| float translateY = getTranslationY(); |
| setTranslationY(0); |
| |
| ViewUtils.gatherTransparentRegionsForOpaqueView(this, region); |
| |
| setTranslationY(translateY); |
| |
| return true; |
| } |
| |
| /** |
| * Invalidate the entire capturing bitmap region. |
| */ |
| public void invalidateBitmap() { |
| ((ToolbarViewResourceAdapter) getToolbarResourceAdapter()).forceInvalidate(); |
| } |
| |
| /** |
| * Update whether the control container is ready to have the bitmap representation of |
| * itself be captured. |
| */ |
| public void setReadyForBitmapCapture(boolean ready) { |
| mToolbarContainer.mReadyForBitmapCapture = ready; |
| } |
| |
| /** |
| * The layout that handles generating the toolbar view resource. |
| */ |
| // Only publicly visible due to lint warnings. |
| public static class ToolbarViewResourceFrameLayout extends ViewResourceFrameLayout { |
| private boolean mReadyForBitmapCapture; |
| |
| public ToolbarViewResourceFrameLayout(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| @Override |
| protected ViewResourceAdapter createResourceAdapter() { |
| return new ToolbarViewResourceAdapter(this); |
| } |
| |
| public void setToolbar(Toolbar toolbar) { |
| ((ToolbarViewResourceAdapter) getResourceAdapter()).setToolbar(toolbar); |
| } |
| |
| @Override |
| protected boolean isReadyForCapture() { |
| return mReadyForBitmapCapture; |
| } |
| } |
| |
| private static class ToolbarViewResourceAdapter extends ViewResourceAdapter { |
| private final int[] mTempPosition = new int[2]; |
| private final Rect mLocationBarRect = new Rect(); |
| private final Rect mToolbarRect = new Rect(); |
| private final View mToolbarContainer; |
| |
| private Toolbar mToolbar; |
| private int mTabStripHeightPx; |
| |
| /** Builds the resource adapter for the toolbar. */ |
| public ToolbarViewResourceAdapter(View toolbarContainer) { |
| super(toolbarContainer); |
| mToolbarContainer = toolbarContainer; |
| } |
| |
| /** |
| * Set the toolbar after it has been dynamically inflated. |
| * @param toolbar The browser's toolbar. |
| */ |
| public void setToolbar(Toolbar toolbar) { |
| mToolbar = toolbar; |
| mTabStripHeightPx = mToolbarContainer.getResources().getDimensionPixelSize( |
| R.dimen.tab_strip_height); |
| } |
| |
| /** |
| * Force this resource to be recaptured in full, ignoring the checks |
| * {@link #invalidate(Rect)} does. |
| */ |
| public void forceInvalidate() { |
| super.invalidate(null); |
| } |
| |
| @Override |
| public boolean isDirty() { |
| return mToolbar != null && mToolbar.isReadyForTextureCapture() && super.isDirty(); |
| } |
| |
| @Override |
| protected void onCaptureStart(Canvas canvas, Rect dirtyRect) { |
| // Erase the canvas because assets drawn are not fully opaque and therefore painting |
| // twice would be bad. |
| canvas.save(); |
| canvas.clipRect( |
| 0, 0, |
| mToolbarContainer.getWidth(), mToolbarContainer.getHeight()); |
| canvas.drawColor(0, PorterDuff.Mode.CLEAR); |
| canvas.restore(); |
| dirtyRect.set(0, 0, mToolbarContainer.getWidth(), mToolbarContainer.getHeight()); |
| |
| mToolbar.setTextureCaptureMode(true); |
| |
| super.onCaptureStart(canvas, dirtyRect); |
| } |
| |
| @Override |
| protected void onCaptureEnd() { |
| mToolbar.setTextureCaptureMode(false); |
| // Forcing a texture capture should only be done for one draw. Turn off forced |
| // texture capture. |
| mToolbar.setForceTextureCapture(false); |
| } |
| |
| @Override |
| public long createNativeResource() { |
| mToolbar.getPositionRelativeToContainer(mToolbarContainer, mTempPosition); |
| mToolbarRect.set(mTempPosition[0], mTempPosition[1], mToolbarContainer.getWidth(), |
| mTempPosition[1] + mToolbar.getHeight()); |
| |
| mToolbar.getLocationBarContentRect(mLocationBarRect); |
| mLocationBarRect.offset(mTempPosition[0], mTempPosition[1]); |
| |
| int shadowHeight = |
| mToolbarContainer.getHeight() - mToolbar.getHeight() - mTabStripHeightPx; |
| return ResourceFactory.createToolbarContainerResource( |
| mToolbarRect, mLocationBarRect, shadowHeight); |
| } |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent event) { |
| // Don't eat the event if we don't have a handler. |
| if (mSwipeHandler == null) return false; |
| |
| // If we have ACTION_DOWN in this context, that means either no child consumed the event or |
| // this class is the top UI at the event position. Then, we don't need to feed the event to |
| // mGestureDetector here because the event is already once fed in onInterceptTouchEvent(). |
| // Moreover, we have to return true so that this class can continue to intercept all the |
| // subsequent events. |
| if (event.getActionMasked() == MotionEvent.ACTION_DOWN && !isOnTabStrip(event)) { |
| return true; |
| } |
| |
| return mSwipeRecognizer.onTouchEvent(event); |
| } |
| |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent event) { |
| if (mSwipeHandler == null || isOnTabStrip(event)) return false; |
| |
| return mSwipeRecognizer.onTouchEvent(event); |
| } |
| |
| private boolean isOnTabStrip(MotionEvent e) { |
| return e.getY() <= mTabStripHeight; |
| } |
| |
| private class SwipeRecognizerImpl extends SwipeRecognizer { |
| public SwipeRecognizerImpl(Context context) { |
| super(context); |
| } |
| |
| @Override |
| public boolean shouldRecognizeSwipe(MotionEvent e1, MotionEvent e2) { |
| if (isOnTabStrip(e1)) return false; |
| if (mToolbar.shouldIgnoreSwipeGesture()) return false; |
| if (UiUtils.isKeyboardShowing(getContext(), ToolbarControlContainer.this)) return false; |
| return true; |
| } |
| } |
| } |