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