Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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.compositor.layouts;
6
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.graphics.Rect;
10 import android.view.animation.DecelerateInterpolator;
11 import android.view.animation.Interpolator;
12
13 import com.google.android.apps.chrome.R;
14
15 import org.chromium.base.metrics.RecordUserAction;
16 import org.chromium.chrome.browser.Tab;
17 import org.chromium.chrome.browser.compositor.LayerTitleCache;
18 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable ;
19 import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
20 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
21 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEvent Filter.ScrollDirection;
22 import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
23 import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
24 import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
25 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
26 import org.chromium.chrome.browser.tabmodel.TabModel;
27 import org.chromium.chrome.browser.util.MathUtils;
28 import org.chromium.ui.base.LocalizationUtils;
29 import org.chromium.ui.resources.ResourceManager;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 /**
35 * Layout defining the animation and positioning of the tabs during the edge swi pe effect.
36 */
37 public class ToolbarSwipeLayout extends Layout implements Animatable<ToolbarSwip eLayout.Property> {
38 /**
39 * Animation properties
40 */
41 public enum Property {
42 OFFSET,
43 }
44
45 private static final boolean ANONYMIZE_NON_FOCUSED_TAB = true;
46
47 // Unit is millisecond / screen.
48 private static final float ANIMATION_SPEED_SCREEN = 500.0f;
49
50 // This is the time step used to move the offset based on fling
51 private static final float FLING_TIME_STEP = 1.0f / 30.0f;
52
53 // This is the max contribution from fling in screen size percentage.
54 private static final float FLING_MAX_CONTRIBUTION = 0.5f;
55
56 private LayoutTab mLeftTab;
57 private LayoutTab mRightTab;
58 private LayoutTab mFromTab; // Set to either mLeftTab or mRightTab.
59 private LayoutTab mToTab; // Set to mLeftTab or mRightTab or null if it is n ot determined.
60
61 // Whether or not to show the toolbar.
62 private boolean mMoveToolbar;
63
64 // Offsets are in pixels [0, width].
65 private float mOffsetStart;
66 private float mOffset;
67 private float mOffsetTarget;
68
69 // These will be set from dimens.xml
70 private final float mSpaceBetweenTabs;
71 private final float mCommitDistanceFromEdge;
72
73 private final TabListSceneLayer mSceneLayer;
74
75 private final Interpolator mEdgeInterpolator = new DecelerateInterpolator();
76
77 /**
78 * @param context The current Android's context.
79 * @param updateHost The {@link LayoutUpdateHost} view for this lay out.
80 * @param renderHost The {@link LayoutRenderHost} view for this lay out.
81 * @param eventFilter The {@link EventFilter} that is needed for thi s view.
82 */
83 public ToolbarSwipeLayout(Context context, LayoutUpdateHost updateHost,
84 LayoutRenderHost renderHost, EventFilter eventFilter) {
85 super(context, updateHost, renderHost, eventFilter);
86 Resources res = context.getResources();
87 final float pxToDp = 1.0f / res.getDisplayMetrics().density;
88 mCommitDistanceFromEdge = res.getDimension(R.dimen.toolbar_swipe_commit_ distance) * pxToDp;
89 mSpaceBetweenTabs = res.getDimension(R.dimen.toolbar_swipe_space_between _tabs) * pxToDp;
90 mSceneLayer = new TabListSceneLayer();
91 }
92
93 /**
94 * @param moveToolbar Whether or not swiping this layout should also move th e toolbar as well as
95 * the content.
96 */
97 public void setMovesToolbar(boolean moveToolbar) {
98 mMoveToolbar = moveToolbar;
99 }
100
101 @Override
102 public int getSizingFlags() {
103 return mMoveToolbar ? SizingFlags.HELPER_HIDE_TOOLBAR_IMMEDIATE
104 : SizingFlags.HELPER_NO_FULLSCREEN_SUPPORT;
105 }
106
107 @Override
108 public void show(long time, boolean animate) {
109 super.show(time, animate);
110 init();
111 if (mTabModelSelector == null) return;
112 Tab tab = mTabModelSelector.getCurrentTab();
113 if (tab != null && tab.isNativePage()) mTabContentManager.cacheTabThumbn ail(tab);
114
115 TabModel model = mTabModelSelector.getCurrentModel();
116 if (model == null) return;
117 int fromTabId = mTabModelSelector.getCurrentTabId();
118 if (fromTabId == TabModel.INVALID_TAB_INDEX) return;
119 mFromTab = createLayoutTab(fromTabId, model.isIncognito(), NO_CLOSE_BUTT ON, NEED_TITLE);
120 prepareLayoutTabForSwipe(mFromTab, false);
121 }
122
123 @Override
124 public void swipeStarted(long time, ScrollDirection direction, float x, floa t y) {
125 if (mTabModelSelector == null || mToTab != null || direction == ScrollDi rection.DOWN) {
126 return;
127 }
128
129 boolean dragFromLeftEdge = direction == ScrollDirection.RIGHT;
130 // Finish off any other animations.
131 forceAnimationToFinish();
132
133 // Determine which tabs we're showing.
134 TabModel model = mTabModelSelector.getCurrentModel();
135 if (model == null) return;
136 int fromIndex = model.index();
137 if (fromIndex == TabModel.INVALID_TAB_INDEX) return;
138
139 // On RTL, edge-dragging to the left is the next tab.
140 int toIndex = (LocalizationUtils.isLayoutRtl() ^ dragFromLeftEdge) ? fro mIndex - 1
141 : fro mIndex + 1;
142 int leftIndex = dragFromLeftEdge ? toIndex : fromIndex;
143 int rightIndex = !dragFromLeftEdge ? toIndex : fromIndex;
144
145 List<Integer> visibleTabs = new ArrayList<Integer>();
146 if (0 <= leftIndex && leftIndex < model.getCount()) {
147 int leftTabId = model.getTabAt(leftIndex).getId();
148 mLeftTab = createLayoutTab(leftTabId, model.isIncognito(), NO_CLOSE_ BUTTON, NEED_TITLE);
149 prepareLayoutTabForSwipe(mLeftTab, leftIndex != fromIndex);
150 visibleTabs.add(leftTabId);
151 }
152 if (0 <= rightIndex && rightIndex < model.getCount()) {
153 int rightTabId = model.getTabAt(rightIndex).getId();
154 mRightTab =
155 createLayoutTab(rightTabId, model.isIncognito(), NO_CLOSE_BU TTON, NEED_TITLE);
156 prepareLayoutTabForSwipe(mRightTab, rightIndex != fromIndex);
157 visibleTabs.add(rightTabId);
158 }
159
160 updateCacheVisibleIds(visibleTabs);
161
162 mToTab = null;
163
164 // Reset the tab offsets.
165 mOffsetStart = dragFromLeftEdge ? 0 : getWidth();
166 mOffset = 0;
167 mOffsetTarget = 0;
168
169 if (mLeftTab != null && mRightTab != null) {
170 mLayoutTabs = new LayoutTab[] {mLeftTab, mRightTab};
171 } else if (mLeftTab != null) {
172 mLayoutTabs = new LayoutTab[] {mLeftTab};
173 } else if (mRightTab != null) {
174 mLayoutTabs = new LayoutTab[] {mRightTab};
175 } else {
176 mLayoutTabs = null;
177 }
178
179 requestUpdate();
180 }
181
182 private void prepareLayoutTabForSwipe(LayoutTab layoutTab, boolean anonymize Toolbar) {
183 assert layoutTab != null;
184 if (layoutTab.shouldStall()) layoutTab.setSaturation(0.0f);
185 layoutTab.setScale(1.f);
186 layoutTab.setBorderScale(1.f);
187 layoutTab.setDecorationAlpha(0.f);
188 layoutTab.setY(0.f);
189 layoutTab.setShowToolbar(mMoveToolbar);
190 layoutTab.setAnonymizeToolbar(anonymizeToolbar && ANONYMIZE_NON_FOCUSED_ TAB);
191 }
192
193 @Override
194 public void swipeUpdated(long time, float x, float y, float dx, float dy, fl oat tx, float ty) {
195 mOffsetTarget = MathUtils.clamp(mOffsetStart + tx, 0, getWidth()) - mOff setStart;
196 requestUpdate();
197 }
198
199 @Override
200 public void swipeFlingOccurred(
201 long time, float x, float y, float tx, float ty, float vx, float vy) {
202 // Use the velocity to add on final step which simulate a fling.
203 final float kickRangeX = getWidth() * FLING_MAX_CONTRIBUTION;
204 final float kickRangeY = getHeight() * FLING_MAX_CONTRIBUTION;
205 final float kickX = MathUtils.clamp(vx * FLING_TIME_STEP, -kickRangeX, k ickRangeX);
206 final float kickY = MathUtils.clamp(vy * FLING_TIME_STEP, -kickRangeY, k ickRangeY);
207 swipeUpdated(time, x, y, 0, 0, tx + kickX, ty + kickY);
208 }
209
210 @Override
211 public void swipeFinished(long time) {
212 if (mFromTab == null || mTabModelSelector == null) return;
213
214 // Figures out the tab to snap to and how to animate to it.
215 float commitDistance = Math.min(mCommitDistanceFromEdge, getWidth() / 3) ;
216 float offsetTo = 0;
217 mToTab = mFromTab;
218 if (mOffsetTarget > commitDistance && mLeftTab != null) {
219 mToTab = mLeftTab;
220 offsetTo += getWidth();
221 } else if (mOffsetTarget < -commitDistance && mRightTab != null) {
222 mToTab = mRightTab;
223 offsetTo -= getWidth();
224 }
225
226 if (mToTab != mFromTab) {
227 RecordUserAction.record("MobileSideSwipeFinished");
228 }
229
230 startHiding(mToTab.getId(), false);
231
232 // Animate gracefully the end of the swiping effect.
233 forceAnimationToFinish();
234 float start = mOffsetTarget;
235 float end = offsetTo;
236 long duration = (long) (ANIMATION_SPEED_SCREEN * Math.abs(start - end) / getWidth());
237 if (duration > 0) {
238 addToAnimation(this, Property.OFFSET, start, end, duration, 0);
239 }
240
241 requestRender();
242 }
243
244 @Override
245 public void swipeCancelled(long time) {
246 swipeFinished(time);
247 }
248
249 @Override
250 protected void updateLayout(long time, long dt) {
251 super.updateLayout(time, dt);
252
253 if (mFromTab == null) return;
254 // In case the draw function get called before swipeStarted()
255 if (mLeftTab == null && mRightTab == null) mRightTab = mFromTab;
256
257 mOffset = smoothInput(mOffset, mOffsetTarget);
258 boolean needUpdate = Math.abs(mOffset - mOffsetTarget) >= 0.1f;
259
260 float rightX = 0.0f;
261 float leftX = 0.0f;
262
263 final boolean doEdge = mLeftTab != null ^ mRightTab != null;
264
265 if (doEdge) {
266 float progress = mOffset / getWidth();
267 float direction = Math.signum(progress);
268 float smoothedProgress = mEdgeInterpolator.getInterpolation(Math.abs (progress));
269
270 float maxSlide = getWidth() / 5.f;
271 rightX = direction * smoothedProgress * maxSlide;
272 leftX = rightX;
273 } else {
274 float progress = mOffset / getWidth();
275 progress += mOffsetStart == 0.0f ? 0.0f : 1.0f;
276 progress = MathUtils.clamp(progress, 0.0f, 1.0f);
277
278 assert mLeftTab != null;
279 assert mRightTab != null;
280 rightX = MathUtils.interpolate(0.0f, getWidth() + mSpaceBetweenTabs, progress);
281 // The left tab must be aligned on the right if the image is smaller than the screen.
282 leftX = rightX - mSpaceBetweenTabs
283 - Math.min(getWidth(), mLeftTab.getOriginalContentWidth());
284 // Compute final x post scale and ensure the tab's center point neve r passes the
285 // center point of the screen.
286 float screenCenterX = getWidth() / 2;
287 rightX = Math.max(screenCenterX - mRightTab.getFinalContentWidth() / 2, rightX);
288 leftX = Math.min(screenCenterX - mLeftTab.getFinalContentWidth() / 2 , leftX);
289 }
290
291 if (mLeftTab != null) {
292 mLeftTab.setX(leftX);
293 needUpdate = mLeftTab.updateSnap(dt) || needUpdate;
294 }
295
296 if (mRightTab != null) {
297 mRightTab.setX(rightX);
298 needUpdate = mRightTab.updateSnap(dt) || needUpdate;
299 }
300 if (needUpdate) requestUpdate();
301 }
302
303 /**
304 * Smoothes input signal. The definition of the input is lower than the
305 * pixel density of the screen so we need to smooth the input to give the il lusion of smooth
306 * animation on screen from chunky inputs.
307 * The combination of 30 pixels and 0.8f ensures that the output is not more than 6 pixels away
308 * from the target.
309 * TODO(dtrainor): This has nothing to do with time, just draw rate.
310 * Is this okay or do we want to have the interpolation based on the t ime elapsed?
311 * @param current The current value of the signal.
312 * @param input The raw input value.
313 * @return The smoothed signal.
314 */
315 private float smoothInput(float current, float input) {
316 current = MathUtils.clamp(current, input - 30, input + 30);
317 return MathUtils.interpolate(current, input, 0.8f);
318 }
319
320 private void init() {
321 mLayoutTabs = null;
322 mFromTab = null;
323 mLeftTab = null;
324 mRightTab = null;
325 mToTab = null;
326 mOffsetStart = 0;
327 mOffset = 0;
328 mOffsetTarget = 0;
329 }
330
331 /**
332 * Sets a property for an animation.
333 * @param prop The property to update
334 * @param value New value of the property
335 */
336 @Override
337 public void setProperty(Property prop, float value) {
338 if (prop == Property.OFFSET) {
339 mOffset = value;
340 mOffsetTarget = mOffset;
341 }
342 }
343
344 @Override
345 protected SceneLayer getSceneLayer() {
346 return mSceneLayer;
347 }
348
349 @Override
350 protected void updateSceneLayer(Rect viewport, Rect contentViewport,
351 LayerTitleCache layerTitleCache, TabContentManager tabContentManager ,
352 ResourceManager resourceManager, ChromeFullscreenManager fullscreenM anager) {
353 super.updateSceneLayer(viewport, contentViewport, layerTitleCache, tabCo ntentManager,
354 resourceManager, fullscreenManager);
355 assert mSceneLayer != null;
356 mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, la yerTitleCache,
357 tabContentManager, resourceManager);
358 }
359 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698