Commit 54202132 authored by Gang Wu's avatar Gang Wu Committed by Commit Bot

[Omnibox] Implement the animation for tab switch

This CL implement the animation for tab switch from Omnibox suggestions.
Try to match the animation in IOS, if new tab's index is smaller than
old one, new tab slide in from left. If new tab's index is larger than
old one, new tab slide in from right.
Also, the animation time is matching IOS at
https://source.chromium.org/chromium/chromium/src/+/master:ios/chrome/browser/ui/tabs/switch_to_tab_animation_view.mm;l=15.


Bug:1092268, 1101517

Change-Id: I5b6c6fa8c290130a3f7fdbc9818a2ac40179cfef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2276806
Commit-Queue: Gang Wu <gangwu@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#785758}
parent 3d835bec
......@@ -156,7 +156,11 @@ public class LayoutManager implements LayoutUpdateHost, LayoutProvider,
protected class LayoutManagerTabModelObserver implements TabModelObserver {
@Override
public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) {
if (tab.getId() != lastId) tabSelected(tab.getId(), lastId, tab.isIncognito());
if (type == TabSelectionType.FROM_OMNIBOX) {
switchToTab(tab, lastId);
} else if (tab.getId() != lastId) {
tabSelected(tab.getId(), lastId, tab.isIncognito());
}
mCurrentTab = tab;
}
......@@ -986,4 +990,14 @@ public class LayoutManager implements LayoutUpdateHost, LayoutProvider,
public LayoutTab getLayoutTabForTesting(int tabId) {
return mTabCache.get(tabId);
}
/**
* Should be called when a tab switch event is triggered, only can switch to the Tab which in
* the current TabModel.
* @param tab The tab that will be switched to.
* @param lastTabId The id of the tab that was switched from.
*/
protected void switchToTab(Tab tab, int lastTabId) {
tabSelected(tab.getId(), lastTabId, tab.isIncognito());
}
}
......@@ -26,6 +26,7 @@ import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutHelperMa
import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
import org.chromium.chrome.browser.device.DeviceClassManager;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
......@@ -550,4 +551,21 @@ public class LayoutManagerChrome
TabModelSelector selector = getTabModelSelector();
return selector == null ? null : selector.getTabById(id);
}
@Override
protected void switchToTab(Tab tab, int lastTabId) {
if (tab == null || lastTabId == Tab.INVALID_TAB_ID) {
super.switchToTab(tab, lastTabId);
return;
}
startShowing(mToolbarSwipeLayout, false);
mToolbarSwipeLayout.switchToTab(tab.getId(), lastTabId);
// Close the previous tab if the previous tab is a NTP.
Tab lastTab = getTabById(lastTabId);
if (NewTabPage.isNTPUrl(lastTab.getUrl()) && !lastTab.canGoBack()
&& !lastTab.canGoForward()) {
getTabModelSelector().getCurrentModel().closeTab(lastTab, tab, false, false, false);
}
}
}
......@@ -23,6 +23,7 @@ import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelUtils;
import org.chromium.components.browser_ui.widget.animation.Interpolators;
import org.chromium.ui.base.LocalizationUtils;
import org.chromium.ui.resources.ResourceManager;
......@@ -37,7 +38,10 @@ public class ToolbarSwipeLayout extends Layout {
private static final boolean ANONYMIZE_NON_FOCUSED_TAB = true;
// Unit is millisecond / screen.
private static final float ANIMATION_SPEED_SCREEN = 500.0f;
private static final float ANIMATION_SPEED_SCREEN_MS = 500.0f;
// The time duration of the animation for switch to tab, Unit is millisecond.
private static final long SWITCH_TO_TAB_DURATION_MS = 350;
// This is the time step used to move the offset based on fling
private static final float FLING_TIME_STEP = 1.0f / 30.0f;
......@@ -140,11 +144,27 @@ public class ToolbarSwipeLayout extends Layout {
// On RTL, edge-dragging to the left is the next tab.
int toIndex = (LocalizationUtils.isLayoutRtl() ^ dragFromLeftEdge) ? fromIndex - 1
: fromIndex + 1;
prepareSwipeTabAnimation(direction, fromIndex, toIndex);
}
/**
* Prepare the tabs sliding animations. This method need to be called before
* {@link #doTabSwitchAnimation(int, float, float, long)}.
* @param direction The direction of the slide.
* @param fromIndex The index of the tab which will be switched from.
* @param toIndex The index of the tab which will be switched to.
*/
private void prepareSwipeTabAnimation(
@ScrollDirection int direction, int fromIndex, int toIndex) {
boolean dragFromLeftEdge = direction == ScrollDirection.RIGHT;
int leftIndex = dragFromLeftEdge ? toIndex : fromIndex;
int rightIndex = !dragFromLeftEdge ? toIndex : fromIndex;
int leftTabId = Tab.INVALID_TAB_ID;
int rightTabId = Tab.INVALID_TAB_ID;
TabModel model = mTabModelSelector.getCurrentModel();
if (0 <= leftIndex && leftIndex < model.getCount()) {
leftTabId = model.getTabAt(leftIndex).getId();
mLeftTab = createLayoutTab(leftTabId, model.isIncognito(), NO_CLOSE_BUTTON, NEED_TITLE);
......@@ -233,20 +253,33 @@ public class ToolbarSwipeLayout extends Layout {
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) {
CompositorAnimator offsetAnimation =
CompositorAnimator.ofFloat(getAnimationHandler(), start, end, duration, null);
offsetAnimation.addUpdateListener(animator -> {
mOffset = animator.getAnimatedValue();
mOffsetTarget = mOffset;
});
offsetAnimation.start();
}
long duration = (long) (ANIMATION_SPEED_SCREEN_MS * Math.abs(start - end) / getWidth());
doTabSwitchAnimation(mToTab.getId(), start, end, duration);
}
/**
* Perform the tabs sliding animations. {@link #prepareSwipeTabAnimation(int, int, int)} need to
* be called before calling this method.
* @param tabId The id of the tab which will be switched to.
* @param start The start point of X coordinate for the animation.
* @param end The end point of X coordinate for the animation.
* @param duration The animation duration in millisecond.
*/
private void doTabSwitchAnimation(int tabId, float start, float end, long duration) {
// Animate gracefully the end of the swiping effect.
forceAnimationToFinish();
if (duration <= 0) return;
CompositorAnimator offsetAnimation =
CompositorAnimator.ofFloat(getAnimationHandler(), start, end, duration, null);
offsetAnimation.addUpdateListener(animator -> {
mOffset = animator.getAnimatedValue();
mOffsetTarget = mOffset;
});
offsetAnimation.start();
}
public void swipeCancelled(long time) {
......@@ -359,4 +392,25 @@ public class ToolbarSwipeLayout extends Layout {
layerTitleCache, tabContentManager, resourceManager, fullscreenManager,
SceneLayer.INVALID_RESOURCE_ID, 0, 0);
}
/**
* Perform the tabs sliding animations. If the new tab's index is smaller than the old one, new
* tab slide in from left, and old one slide out to right, and vice versa.
* @param toTabId The id of the next tab which will be switched to.
* @param fromTabId The id of the previous tab which will be switched out.
*/
public void switchToTab(int toTabId, int fromTabId) {
int fromTabIndex =
TabModelUtils.getTabIndexById(mTabModelSelector.getCurrentModel(), fromTabId);
int toTabIndex =
TabModelUtils.getTabIndexById(mTabModelSelector.getCurrentModel(), toTabId);
prepareSwipeTabAnimation(
fromTabIndex < toTabIndex ? ScrollDirection.LEFT : ScrollDirection.RIGHT,
fromTabIndex, toTabIndex);
mToTab = fromTabIndex < toTabIndex ? mRightTab : mLeftTab;
float end = fromTabIndex < toTabIndex ? -getWidth() : getWidth();
startHiding(toTabId, false);
doTabSwitchAnimation(toTabId, 0f, end, SWITCH_TO_TAB_DURATION_MS);
}
}
......@@ -22,6 +22,7 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.IntentUtils;
import org.chromium.base.Log;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
......@@ -586,13 +587,12 @@ class AutocompleteMediator implements OnSuggestionsReceivedListener, StartStopWi
int tabIndex = TabModelUtils.getTabIndexById(
chromeActivity.getTabModelSelector().getCurrentModel(), tab.getId());
chromeActivity.getTabModelSelector().getCurrentModel().setIndex(
tabIndex, TabSelectionType.FROM_USER);
// TODO(crbug.com/1092268): Add animation.
tabIndex, TabSelectionType.FROM_OMNIBOX);
} else {
Intent newIntent = ChromeIntentUtil.createBringTabToFrontIntent(tab.getId());
if (newIntent != null) {
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ContextUtils.getApplicationContext().startActivity(newIntent);
IntentUtils.safeStartActivity(ContextUtils.getApplicationContext(), newIntent);
}
}
......
......@@ -101,6 +101,9 @@ class TabModel {
// User-originated switch to existing tab or selection of main tab on app
// startup.
FROM_USER,
// User-originated switch to existing tab from Omnibox tab switch
// suggestions.
FROM_OMNIBOX,
// Must be last.
SIZE
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment