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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
new file mode 100644
index 0000000000000000000000000000000000000000..91508d4a0646e1d2609b293b58647de1cf4b7045
--- /dev/null
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -0,0 +1,359 @@
+// 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.layouts;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.google.android.apps.chrome.R;
+
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.Tab;
+import org.chromium.chrome.browser.compositor.LayerTitleCache;
+import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation.Animatable;
+import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EdgeSwipeEventFilter.ScrollDirection;
+import org.chromium.chrome.browser.compositor.layouts.eventfilter.EventFilter;
+import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer;
+import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
+import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.util.MathUtils;
+import org.chromium.ui.base.LocalizationUtils;
+import org.chromium.ui.resources.ResourceManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Layout defining the animation and positioning of the tabs during the edge swipe effect.
+ */
+public class ToolbarSwipeLayout extends Layout implements Animatable<ToolbarSwipeLayout.Property> {
+ /**
+ * Animation properties
+ */
+ public enum Property {
+ OFFSET,
+ }
+
+ private static final boolean ANONYMIZE_NON_FOCUSED_TAB = true;
+
+ // Unit is millisecond / screen.
+ private static final float ANIMATION_SPEED_SCREEN = 500.0f;
+
+ // This is the time step used to move the offset based on fling
+ private static final float FLING_TIME_STEP = 1.0f / 30.0f;
+
+ // This is the max contribution from fling in screen size percentage.
+ private static final float FLING_MAX_CONTRIBUTION = 0.5f;
+
+ private LayoutTab mLeftTab;
+ private LayoutTab mRightTab;
+ private LayoutTab mFromTab; // Set to either mLeftTab or mRightTab.
+ private LayoutTab mToTab; // Set to mLeftTab or mRightTab or null if it is not determined.
+
+ // Whether or not to show the toolbar.
+ private boolean mMoveToolbar;
+
+ // Offsets are in pixels [0, width].
+ private float mOffsetStart;
+ private float mOffset;
+ private float mOffsetTarget;
+
+ // These will be set from dimens.xml
+ private final float mSpaceBetweenTabs;
+ private final float mCommitDistanceFromEdge;
+
+ private final TabListSceneLayer mSceneLayer;
+
+ private final Interpolator mEdgeInterpolator = new DecelerateInterpolator();
+
+ /**
+ * @param context The current Android's context.
+ * @param updateHost The {@link LayoutUpdateHost} view for this layout.
+ * @param renderHost The {@link LayoutRenderHost} view for this layout.
+ * @param eventFilter The {@link EventFilter} that is needed for this view.
+ */
+ public ToolbarSwipeLayout(Context context, LayoutUpdateHost updateHost,
+ LayoutRenderHost renderHost, EventFilter eventFilter) {
+ super(context, updateHost, renderHost, eventFilter);
+ Resources res = context.getResources();
+ final float pxToDp = 1.0f / res.getDisplayMetrics().density;
+ mCommitDistanceFromEdge = res.getDimension(R.dimen.toolbar_swipe_commit_distance) * pxToDp;
+ mSpaceBetweenTabs = res.getDimension(R.dimen.toolbar_swipe_space_between_tabs) * pxToDp;
+ mSceneLayer = new TabListSceneLayer();
+ }
+
+ /**
+ * @param moveToolbar Whether or not swiping this layout should also move the toolbar as well as
+ * the content.
+ */
+ public void setMovesToolbar(boolean moveToolbar) {
+ mMoveToolbar = moveToolbar;
+ }
+
+ @Override
+ public int getSizingFlags() {
+ return mMoveToolbar ? SizingFlags.HELPER_HIDE_TOOLBAR_IMMEDIATE
+ : SizingFlags.HELPER_NO_FULLSCREEN_SUPPORT;
+ }
+
+ @Override
+ public void show(long time, boolean animate) {
+ super.show(time, animate);
+ init();
+ if (mTabModelSelector == null) return;
+ Tab tab = mTabModelSelector.getCurrentTab();
+ if (tab != null && tab.isNativePage()) mTabContentManager.cacheTabThumbnail(tab);
+
+ TabModel model = mTabModelSelector.getCurrentModel();
+ if (model == null) return;
+ int fromTabId = mTabModelSelector.getCurrentTabId();
+ if (fromTabId == TabModel.INVALID_TAB_INDEX) return;
+ mFromTab = createLayoutTab(fromTabId, model.isIncognito(), NO_CLOSE_BUTTON, NEED_TITLE);
+ prepareLayoutTabForSwipe(mFromTab, false);
+ }
+
+ @Override
+ public void swipeStarted(long time, ScrollDirection direction, float x, float y) {
+ if (mTabModelSelector == null || mToTab != null || direction == ScrollDirection.DOWN) {
+ return;
+ }
+
+ boolean dragFromLeftEdge = direction == ScrollDirection.RIGHT;
+ // Finish off any other animations.
+ forceAnimationToFinish();
+
+ // Determine which tabs we're showing.
+ TabModel model = mTabModelSelector.getCurrentModel();
+ if (model == null) return;
+ int fromIndex = model.index();
+ if (fromIndex == TabModel.INVALID_TAB_INDEX) return;
+
+ // On RTL, edge-dragging to the left is the next tab.
+ int toIndex = (LocalizationUtils.isLayoutRtl() ^ dragFromLeftEdge) ? fromIndex - 1
+ : fromIndex + 1;
+ int leftIndex = dragFromLeftEdge ? toIndex : fromIndex;
+ int rightIndex = !dragFromLeftEdge ? toIndex : fromIndex;
+
+ List<Integer> visibleTabs = new ArrayList<Integer>();
+ if (0 <= leftIndex && leftIndex < model.getCount()) {
+ int leftTabId = model.getTabAt(leftIndex).getId();
+ mLeftTab = createLayoutTab(leftTabId, model.isIncognito(), NO_CLOSE_BUTTON, NEED_TITLE);
+ prepareLayoutTabForSwipe(mLeftTab, leftIndex != fromIndex);
+ visibleTabs.add(leftTabId);
+ }
+ if (0 <= rightIndex && rightIndex < model.getCount()) {
+ int rightTabId = model.getTabAt(rightIndex).getId();
+ mRightTab =
+ createLayoutTab(rightTabId, model.isIncognito(), NO_CLOSE_BUTTON, NEED_TITLE);
+ prepareLayoutTabForSwipe(mRightTab, rightIndex != fromIndex);
+ visibleTabs.add(rightTabId);
+ }
+
+ updateCacheVisibleIds(visibleTabs);
+
+ mToTab = null;
+
+ // Reset the tab offsets.
+ mOffsetStart = dragFromLeftEdge ? 0 : getWidth();
+ mOffset = 0;
+ mOffsetTarget = 0;
+
+ if (mLeftTab != null && mRightTab != null) {
+ mLayoutTabs = new LayoutTab[] {mLeftTab, mRightTab};
+ } else if (mLeftTab != null) {
+ mLayoutTabs = new LayoutTab[] {mLeftTab};
+ } else if (mRightTab != null) {
+ mLayoutTabs = new LayoutTab[] {mRightTab};
+ } else {
+ mLayoutTabs = null;
+ }
+
+ requestUpdate();
+ }
+
+ private void prepareLayoutTabForSwipe(LayoutTab layoutTab, boolean anonymizeToolbar) {
+ assert layoutTab != null;
+ if (layoutTab.shouldStall()) layoutTab.setSaturation(0.0f);
+ layoutTab.setScale(1.f);
+ layoutTab.setBorderScale(1.f);
+ layoutTab.setDecorationAlpha(0.f);
+ layoutTab.setY(0.f);
+ layoutTab.setShowToolbar(mMoveToolbar);
+ layoutTab.setAnonymizeToolbar(anonymizeToolbar && ANONYMIZE_NON_FOCUSED_TAB);
+ }
+
+ @Override
+ public void swipeUpdated(long time, float x, float y, float dx, float dy, float tx, float ty) {
+ mOffsetTarget = MathUtils.clamp(mOffsetStart + tx, 0, getWidth()) - mOffsetStart;
+ requestUpdate();
+ }
+
+ @Override
+ public void swipeFlingOccurred(
+ long time, float x, float y, float tx, float ty, float vx, float vy) {
+ // Use the velocity to add on final step which simulate a fling.
+ final float kickRangeX = getWidth() * FLING_MAX_CONTRIBUTION;
+ final float kickRangeY = getHeight() * FLING_MAX_CONTRIBUTION;
+ final float kickX = MathUtils.clamp(vx * FLING_TIME_STEP, -kickRangeX, kickRangeX);
+ final float kickY = MathUtils.clamp(vy * FLING_TIME_STEP, -kickRangeY, kickRangeY);
+ swipeUpdated(time, x, y, 0, 0, tx + kickX, ty + kickY);
+ }
+
+ @Override
+ public void swipeFinished(long time) {
+ if (mFromTab == null || mTabModelSelector == null) return;
+
+ // Figures out the tab to snap to and how to animate to it.
+ float commitDistance = Math.min(mCommitDistanceFromEdge, getWidth() / 3);
+ float offsetTo = 0;
+ mToTab = mFromTab;
+ if (mOffsetTarget > commitDistance && mLeftTab != null) {
+ mToTab = mLeftTab;
+ offsetTo += getWidth();
+ } else if (mOffsetTarget < -commitDistance && mRightTab != null) {
+ mToTab = mRightTab;
+ offsetTo -= getWidth();
+ }
+
+ if (mToTab != mFromTab) {
+ RecordUserAction.record("MobileSideSwipeFinished");
+ }
+
+ startHiding(mToTab.getId(), false);
+
+ // Animate gracefully the end of the swiping effect.
+ forceAnimationToFinish();
+ float start = mOffsetTarget;
+ float end = offsetTo;
+ long duration = (long) (ANIMATION_SPEED_SCREEN * Math.abs(start - end) / getWidth());
+ if (duration > 0) {
+ addToAnimation(this, Property.OFFSET, start, end, duration, 0);
+ }
+
+ requestRender();
+ }
+
+ @Override
+ public void swipeCancelled(long time) {
+ swipeFinished(time);
+ }
+
+ @Override
+ protected void updateLayout(long time, long dt) {
+ super.updateLayout(time, dt);
+
+ if (mFromTab == null) return;
+ // In case the draw function get called before swipeStarted()
+ if (mLeftTab == null && mRightTab == null) mRightTab = mFromTab;
+
+ mOffset = smoothInput(mOffset, mOffsetTarget);
+ boolean needUpdate = Math.abs(mOffset - mOffsetTarget) >= 0.1f;
+
+ float rightX = 0.0f;
+ float leftX = 0.0f;
+
+ final boolean doEdge = mLeftTab != null ^ mRightTab != null;
+
+ if (doEdge) {
+ float progress = mOffset / getWidth();
+ float direction = Math.signum(progress);
+ float smoothedProgress = mEdgeInterpolator.getInterpolation(Math.abs(progress));
+
+ float maxSlide = getWidth() / 5.f;
+ rightX = direction * smoothedProgress * maxSlide;
+ leftX = rightX;
+ } else {
+ float progress = mOffset / getWidth();
+ progress += mOffsetStart == 0.0f ? 0.0f : 1.0f;
+ progress = MathUtils.clamp(progress, 0.0f, 1.0f);
+
+ assert mLeftTab != null;
+ assert mRightTab != null;
+ rightX = MathUtils.interpolate(0.0f, getWidth() + mSpaceBetweenTabs, progress);
+ // The left tab must be aligned on the right if the image is smaller than the screen.
+ leftX = rightX - mSpaceBetweenTabs
+ - Math.min(getWidth(), mLeftTab.getOriginalContentWidth());
+ // Compute final x post scale and ensure the tab's center point never passes the
+ // center point of the screen.
+ float screenCenterX = getWidth() / 2;
+ rightX = Math.max(screenCenterX - mRightTab.getFinalContentWidth() / 2, rightX);
+ leftX = Math.min(screenCenterX - mLeftTab.getFinalContentWidth() / 2, leftX);
+ }
+
+ if (mLeftTab != null) {
+ mLeftTab.setX(leftX);
+ needUpdate = mLeftTab.updateSnap(dt) || needUpdate;
+ }
+
+ if (mRightTab != null) {
+ mRightTab.setX(rightX);
+ needUpdate = mRightTab.updateSnap(dt) || needUpdate;
+ }
+ if (needUpdate) requestUpdate();
+ }
+
+ /**
+ * Smoothes input signal. The definition of the input is lower than the
+ * pixel density of the screen so we need to smooth the input to give the illusion of smooth
+ * animation on screen from chunky inputs.
+ * The combination of 30 pixels and 0.8f ensures that the output is not more than 6 pixels away
+ * from the target.
+ * TODO(dtrainor): This has nothing to do with time, just draw rate.
+ * Is this okay or do we want to have the interpolation based on the time elapsed?
+ * @param current The current value of the signal.
+ * @param input The raw input value.
+ * @return The smoothed signal.
+ */
+ private float smoothInput(float current, float input) {
+ current = MathUtils.clamp(current, input - 30, input + 30);
+ return MathUtils.interpolate(current, input, 0.8f);
+ }
+
+ private void init() {
+ mLayoutTabs = null;
+ mFromTab = null;
+ mLeftTab = null;
+ mRightTab = null;
+ mToTab = null;
+ mOffsetStart = 0;
+ mOffset = 0;
+ mOffsetTarget = 0;
+ }
+
+ /**
+ * Sets a property for an animation.
+ * @param prop The property to update
+ * @param value New value of the property
+ */
+ @Override
+ public void setProperty(Property prop, float value) {
+ if (prop == Property.OFFSET) {
+ mOffset = value;
+ mOffsetTarget = mOffset;
+ }
+ }
+
+ @Override
+ protected SceneLayer getSceneLayer() {
+ return mSceneLayer;
+ }
+
+ @Override
+ protected void updateSceneLayer(Rect viewport, Rect contentViewport,
+ LayerTitleCache layerTitleCache, TabContentManager tabContentManager,
+ ResourceManager resourceManager, ChromeFullscreenManager fullscreenManager) {
+ super.updateSceneLayer(viewport, contentViewport, layerTitleCache, tabContentManager,
+ resourceManager, fullscreenManager);
+ assert mSceneLayer != null;
+ mSceneLayer.pushLayers(getContext(), viewport, contentViewport, this, layerTitleCache,
+ tabContentManager, resourceManager);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698