OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.toolbar; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.graphics.Canvas; |
| 9 import android.graphics.PorterDuff; |
| 10 import android.graphics.Rect; |
| 11 import android.graphics.drawable.Drawable; |
| 12 import android.graphics.drawable.LayerDrawable; |
| 13 import android.graphics.drawable.ScaleDrawable; |
| 14 import android.util.AttributeSet; |
| 15 import android.view.MotionEvent; |
| 16 import android.view.View; |
| 17 import android.widget.FrameLayout; |
| 18 |
| 19 import com.google.android.apps.chrome.R; |
| 20 |
| 21 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeHandl
er; |
| 22 import org.chromium.chrome.browser.contextualsearch.SwipeRecognizer; |
| 23 import org.chromium.chrome.browser.widget.ControlContainer; |
| 24 import org.chromium.chrome.browser.widget.SmoothProgressBar; |
| 25 import org.chromium.chrome.browser.widget.SmoothProgressBar.ProgressChangeListen
er; |
| 26 import org.chromium.chrome.browser.widget.ViewResourceFrameLayout; |
| 27 import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; |
| 28 import org.chromium.ui.UiUtils; |
| 29 import org.chromium.ui.resources.dynamics.ViewResourceAdapter; |
| 30 |
| 31 /** |
| 32 * Layout for the browser controls (omnibox, menu, tab strip, etc..). |
| 33 */ |
| 34 public class ToolbarControlContainer extends FrameLayout implements ControlConta
iner { |
| 35 private final float mTabStripHeight; |
| 36 |
| 37 private Toolbar mToolbar; |
| 38 private ToolbarViewResourceFrameLayout mToolbarContainer; |
| 39 private View mMenuBtn; |
| 40 |
| 41 private final SwipeRecognizer mSwipeRecognizer; |
| 42 private EdgeSwipeHandler mSwipeHandler; |
| 43 |
| 44 private FindToolbarManager mFindToolbarManager; |
| 45 |
| 46 private ViewResourceAdapter mProgressResourceAdapter; |
| 47 |
| 48 /** |
| 49 * Constructs a new control container. |
| 50 * <p> |
| 51 * This constructor is used when inflating from XML. |
| 52 * |
| 53 * @param context The context used to build this view. |
| 54 * @param attrs The attributes used to determine how to construct this view. |
| 55 */ |
| 56 public ToolbarControlContainer(Context context, AttributeSet attrs) { |
| 57 super(context, attrs); |
| 58 mTabStripHeight = context.getResources().getDimension(R.dimen.tab_strip_
height); |
| 59 mSwipeRecognizer = new SwipeRecognizerImpl(context); |
| 60 } |
| 61 |
| 62 @Override |
| 63 public ViewResourceAdapter getProgressResourceAdapter() { |
| 64 return mProgressResourceAdapter; |
| 65 } |
| 66 |
| 67 @Override |
| 68 public ViewResourceAdapter getToolbarResourceAdapter() { |
| 69 return mToolbarContainer.getResourceAdapter(); |
| 70 } |
| 71 |
| 72 @Override |
| 73 public void setSwipeHandler(EdgeSwipeHandler handler) { |
| 74 mSwipeHandler = handler; |
| 75 mSwipeRecognizer.setSwipeHandler(handler); |
| 76 } |
| 77 |
| 78 /** |
| 79 * Sets the manager in charge of find in page. |
| 80 */ |
| 81 public void setFindToolbarManager(FindToolbarManager manager) { |
| 82 mFindToolbarManager = manager; |
| 83 } |
| 84 |
| 85 @Override |
| 86 public void onFinishInflate() { |
| 87 mToolbar = (Toolbar) findViewById(R.id.toolbar); |
| 88 mToolbarContainer = (ToolbarViewResourceFrameLayout) findViewById(R.id.t
oolbar_container); |
| 89 mMenuBtn = findViewById(R.id.menu_button); |
| 90 |
| 91 // TODO(yusufo): Get rid of the calls below and avoid casting to the lay
out without making |
| 92 // the interface bigger. |
| 93 SmoothProgressBar progressView = ((ToolbarLayout) mToolbar).getProgressB
ar(); |
| 94 if (progressView != null) { |
| 95 mProgressResourceAdapter = new ProgressViewResourceAdapter(progressV
iew); |
| 96 } |
| 97 |
| 98 if (mToolbar instanceof ToolbarTablet) { |
| 99 // On tablet, draw a fake tab strip and toolbar until the compositor
is ready to draw |
| 100 // the real tab strip. (On phone, the toolbar is made entirely of An
droid views, which |
| 101 // are already initialized.) |
| 102 setBackgroundResource(R.drawable.toolbar_background); |
| 103 } |
| 104 |
| 105 assert mToolbar != null; |
| 106 assert mMenuBtn != null; |
| 107 |
| 108 super.onFinishInflate(); |
| 109 } |
| 110 |
| 111 /** |
| 112 * Invalidate the entire capturing bitmap region. |
| 113 */ |
| 114 public void invalidateBitmap() { |
| 115 ((ToolbarViewResourceAdapter) getToolbarResourceAdapter()).forceInvalida
te(); |
| 116 } |
| 117 |
| 118 /** |
| 119 * Update whether the control container is ready to have the bitmap represen
tation of |
| 120 * itself be captured. |
| 121 */ |
| 122 public void setReadyForBitmapCapture(boolean ready) { |
| 123 mToolbarContainer.mReadyForBitmapCapture = ready; |
| 124 } |
| 125 |
| 126 /** |
| 127 * The layout that handles generating the toolbar view resource. |
| 128 */ |
| 129 // Only publicly visible due to lint warnings. |
| 130 public static class ToolbarViewResourceFrameLayout extends ViewResourceFrame
Layout { |
| 131 private boolean mReadyForBitmapCapture; |
| 132 |
| 133 public ToolbarViewResourceFrameLayout(Context context, AttributeSet attr
s) { |
| 134 super(context, attrs); |
| 135 } |
| 136 |
| 137 @Override |
| 138 protected ViewResourceAdapter createResourceAdapter() { |
| 139 return new ToolbarViewResourceAdapter( |
| 140 this, (Toolbar) findViewById(R.id.toolbar)); |
| 141 } |
| 142 |
| 143 @Override |
| 144 protected boolean isReadyForCapture() { |
| 145 return mReadyForBitmapCapture; |
| 146 } |
| 147 } |
| 148 |
| 149 private static class ProgressViewResourceAdapter extends ViewResourceAdapter |
| 150 implements ProgressChangeListener { |
| 151 |
| 152 private final SmoothProgressBar mProgressView; |
| 153 private final Rect mPreviousDrawBounds = new Rect(); |
| 154 private int mProgressVisibility; |
| 155 private int mProgress; |
| 156 |
| 157 ProgressViewResourceAdapter(SmoothProgressBar progressView) { |
| 158 super(progressView); |
| 159 |
| 160 mProgressView = progressView; |
| 161 mProgressVisibility = mProgressView.getVisibility(); |
| 162 progressView.addProgressChangeListener(this); |
| 163 } |
| 164 |
| 165 @Override |
| 166 public void onProgressChanged(int progress) { |
| 167 if (mProgressVisibility != View.VISIBLE) return; |
| 168 if (progress < mProgress) { |
| 169 mPreviousDrawBounds.setEmpty(); |
| 170 } |
| 171 mProgress = progress; |
| 172 invalidate(null); |
| 173 } |
| 174 |
| 175 @Override |
| 176 public void onProgressVisibilityChanged(int visibility) { |
| 177 if (mProgressVisibility == visibility) return; |
| 178 |
| 179 if (visibility == View.VISIBLE || mProgressVisibility == View.VISIBL
E) { |
| 180 invalidate(null); |
| 181 mPreviousDrawBounds.setEmpty(); |
| 182 } |
| 183 mProgressVisibility = visibility; |
| 184 } |
| 185 |
| 186 @Override |
| 187 protected void onCaptureStart(Canvas canvas, Rect dirtyRect) { |
| 188 canvas.save(); |
| 189 canvas.clipRect( |
| 190 mPreviousDrawBounds.right, 0, |
| 191 mProgressView.getWidth(), mProgressView.getHeight()); |
| 192 canvas.drawColor(0, PorterDuff.Mode.CLEAR); |
| 193 canvas.restore(); |
| 194 |
| 195 super.onCaptureStart(canvas, dirtyRect); |
| 196 } |
| 197 |
| 198 @Override |
| 199 protected void capture(Canvas canvas) { |
| 200 if (mProgressVisibility != View.VISIBLE) { |
| 201 canvas.drawColor(0, PorterDuff.Mode.CLEAR); |
| 202 } else { |
| 203 super.capture(canvas); |
| 204 } |
| 205 } |
| 206 |
| 207 @Override |
| 208 protected void onCaptureEnd() { |
| 209 super.onCaptureEnd(); |
| 210 // If we are unable to get accurate draw bounds, then set the draw b
ounds to |
| 211 // ensure the entire view is cleared. |
| 212 mPreviousDrawBounds.setEmpty(); |
| 213 |
| 214 // The secondary drawable has an alpha component, so track the bound
s of the |
| 215 // primary drawable. This will allow the subsequent draw call to cl
ear the secondary |
| 216 // portion not overlapped by the primary to prevent the alpha compon
ents from |
| 217 // stacking and getting progressively darker. |
| 218 Drawable progressDrawable = mProgressView.getProgressDrawable(); |
| 219 if (progressDrawable instanceof LayerDrawable) { |
| 220 LayerDrawable progressLayerDrawable = (LayerDrawable) progressDr
awable; |
| 221 for (int i = 0; i < progressLayerDrawable.getNumberOfLayers(); i
++) { |
| 222 if (progressLayerDrawable.getId(i) != android.R.id.progress)
continue; |
| 223 Drawable primaryProgressDrawable = progressLayerDrawable.get
Drawable(i); |
| 224 if (!(primaryProgressDrawable instanceof ScaleDrawable)) con
tinue; |
| 225 |
| 226 ((ScaleDrawable) primaryProgressDrawable).getDrawable().copy
Bounds( |
| 227 mPreviousDrawBounds); |
| 228 } |
| 229 } |
| 230 } |
| 231 |
| 232 @Override |
| 233 protected void computeContentPadding(Rect outContentPadding) { |
| 234 super.computeContentPadding(outContentPadding); |
| 235 MarginLayoutParams layoutParams = |
| 236 (MarginLayoutParams) mProgressView.getLayoutParams(); |
| 237 outContentPadding.offset(0, layoutParams.topMargin); |
| 238 } |
| 239 } |
| 240 |
| 241 private static class ToolbarViewResourceAdapter extends ViewResourceAdapter
{ |
| 242 private final int mToolbarActualHeightPx; |
| 243 private final int[] mTempPosition = new int[2]; |
| 244 |
| 245 private final View mToolbarContainer; |
| 246 private final Toolbar mToolbar; |
| 247 |
| 248 /** Builds the resource adapter for the toolbar. */ |
| 249 public ToolbarViewResourceAdapter(View toolbarContainer, Toolbar toolbar
) { |
| 250 super(toolbarContainer); |
| 251 |
| 252 mToolbarContainer = toolbarContainer; |
| 253 mToolbar = toolbar; |
| 254 mToolbarActualHeightPx = toolbarContainer.getResources().getDimensio
nPixelSize( |
| 255 R.dimen.control_container_height); |
| 256 } |
| 257 |
| 258 /** |
| 259 * Force this resource to be recaptured in full, ignoring the checks |
| 260 * {@link #invalidate(Rect)} does. |
| 261 */ |
| 262 public void forceInvalidate() { |
| 263 super.invalidate(null); |
| 264 } |
| 265 |
| 266 @Override |
| 267 public boolean isDirty() { |
| 268 return mToolbar != null && mToolbar.isReadyForTextureCapture() && su
per.isDirty(); |
| 269 } |
| 270 |
| 271 @Override |
| 272 protected void onCaptureStart(Canvas canvas, Rect dirtyRect) { |
| 273 // Erase the shadow component of the bitmap if the clip rect include
d shadow. Because |
| 274 // this region is not opaque painting twice would be bad. |
| 275 if (dirtyRect.intersects( |
| 276 0, mToolbarActualHeightPx, |
| 277 mToolbarContainer.getWidth(), mToolbarContainer.getHeight())
) { |
| 278 canvas.save(); |
| 279 canvas.clipRect( |
| 280 0, mToolbarActualHeightPx, |
| 281 mToolbarContainer.getWidth(), mToolbarContainer.getHeigh
t()); |
| 282 canvas.drawColor(0, PorterDuff.Mode.CLEAR); |
| 283 canvas.restore(); |
| 284 } |
| 285 |
| 286 mToolbar.setTextureCaptureMode(true); |
| 287 |
| 288 super.onCaptureStart(canvas, dirtyRect); |
| 289 } |
| 290 |
| 291 @Override |
| 292 protected void onCaptureEnd() { |
| 293 mToolbar.setTextureCaptureMode(false); |
| 294 } |
| 295 |
| 296 @Override |
| 297 protected void computeContentPadding(Rect outContentPadding) { |
| 298 outContentPadding.set(0, 0, mToolbarContainer.getWidth(), mToolbarAc
tualHeightPx); |
| 299 } |
| 300 |
| 301 @Override |
| 302 protected void computeContentAperture(Rect outContentAperture) { |
| 303 mToolbar.getLocationBarContentRect(outContentAperture); |
| 304 mToolbar.getPositionRelativeToContainer(mToolbarContainer, mTempPosi
tion); |
| 305 outContentAperture.offset(mTempPosition[0], mTempPosition[1]); |
| 306 } |
| 307 } |
| 308 |
| 309 @Override |
| 310 public boolean onTouchEvent(MotionEvent event) { |
| 311 // Don't eat the event if we don't have a handler. |
| 312 if (mSwipeHandler == null) return false; |
| 313 |
| 314 // If we have ACTION_DOWN in this context, that means either no child co
nsumed the event or |
| 315 // this class is the top UI at the event position. Then, we don't need t
o feed the event to |
| 316 // mGestureDetector here because the event is already once fed in onInte
rceptTouchEvent(). |
| 317 // Moreover, we have to return true so that this class can continue to i
ntercept all the |
| 318 // subsequent events. |
| 319 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && !isOnTabStrip(
event)) { |
| 320 return true; |
| 321 } |
| 322 |
| 323 return mSwipeRecognizer.onTouchEvent(event); |
| 324 } |
| 325 |
| 326 @Override |
| 327 public boolean onInterceptTouchEvent(MotionEvent event) { |
| 328 if (mSwipeHandler == null) return false; |
| 329 |
| 330 return mSwipeRecognizer.onTouchEvent(event); |
| 331 } |
| 332 |
| 333 private boolean isOnTabStrip(MotionEvent e) { |
| 334 return e.getY() <= mTabStripHeight; |
| 335 } |
| 336 |
| 337 private class SwipeRecognizerImpl extends SwipeRecognizer { |
| 338 public SwipeRecognizerImpl(Context context) { |
| 339 super(context); |
| 340 } |
| 341 |
| 342 @Override |
| 343 public boolean shouldRecognizeSwipe(MotionEvent e1, MotionEvent e2) { |
| 344 if (isOnTabStrip(e1)) return false; |
| 345 if (mToolbar.shouldIgnoreSwipeGesture()) return false; |
| 346 if (UiUtils.isKeyboardShowing(getContext(), ToolbarControlContainer.
this)) return false; |
| 347 if (mFindToolbarManager == null || mFindToolbarManager.isShowing())
return false; |
| 348 return true; |
| 349 } |
| 350 } |
| 351 } |
OLD | NEW |