Commit 34381087 authored by Patrick Noland's avatar Patrick Noland Committed by Commit Bot

Remove accidentally restored TabGroup/BottomToolbar files

Change-Id: Icd08d8fde181ec3e867704d3e6a06278cad2c97b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2414619Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808386}
parent e2b4d431
// Copyright 2019 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.tasks.tab_management;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.CallbackController;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
import org.chromium.chrome.tab_ui.R;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.modelutil.PropertyModel;
import java.util.List;
/**
* A mediator for the TabGroupPopUi. Responsible for managing the
* internal state of the component.
*/
public class TabGroupPopupUiMediator {
/**
* An interface to update the size of the TabGroupPopupUi.
*/
public interface TabGroupPopUiUpdater {
/**
* Update the TabGroupPopUi.
*/
void updateTabGroupPopUi();
}
private final PropertyModel mModel;
private final TabModelObserver mTabModelObserver;
private final TabModelSelector mTabModelSelector;
private final BrowserControlsStateProvider mBrowserControlsStateProvider;
private final BrowserControlsStateProvider.Observer mBrowserControlsObserver;
private final KeyboardVisibilityDelegate.KeyboardVisibilityListener mKeyboardVisibilityListener;
private final TabGroupPopUiUpdater mUiUpdater;
private final TabGroupUiMediator.TabGroupUiController mTabGroupUiController;
private final BottomSheetController mBottomSheetController;
private final BottomSheetObserver mBottomSheetObserver;
private final OverviewModeBehavior.OverviewModeObserver mOverviewModeObserver;
private CallbackController mCallbackController = new CallbackController();
private OverviewModeBehavior mOverviewModeBehavior;
private boolean mIsOverviewModeVisible;
TabGroupPopupUiMediator(PropertyModel model, TabModelSelector tabModelSelector,
OneshotSupplier<OverviewModeBehavior> overviewModeBehaviorSupplier,
BrowserControlsStateProvider browserControlsStateProvider, TabGroupPopUiUpdater updater,
TabGroupUiMediator.TabGroupUiController controller,
BottomSheetController bottomSheetController) {
mModel = model;
mTabModelSelector = tabModelSelector;
mBrowserControlsStateProvider = browserControlsStateProvider;
mUiUpdater = updater;
mTabGroupUiController = controller;
mBottomSheetController = bottomSheetController;
mBrowserControlsObserver = new BrowserControlsStateProvider.Observer() {
@Override
public void onControlsOffsetChanged(int topOffset, int topControlsMinHeightOffset,
int bottomOffset, int bottomControlsMinHeightOffset, boolean needsAnimate) {
// Modify the alpha the strip container view base on bottomOffset. The range of
// bottomOffset is within 0 to mIconSize.
mModel.set(TabGroupPopupUiProperties.CONTENT_VIEW_ALPHA,
1 - mBrowserControlsStateProvider.getBrowserControlHiddenRatio());
}
};
mBrowserControlsStateProvider.addObserver(mBrowserControlsObserver);
mTabModelObserver = new TabModelObserver() {
@Override
public void didSelectTab(Tab tab, int type, int lastId) {
List<Tab> tabList = mTabModelSelector.getTabModelFilterProvider()
.getCurrentTabModelFilter()
.getRelatedTabList(lastId);
if (tabList.contains(tab)) return;
if (isCurrentTabInGroup()) {
if (isTabStripShowing()) {
mUiUpdater.updateTabGroupPopUi();
} else {
maybeShowTabStrip();
}
} else {
hideTabStrip();
}
}
@Override
public void willCloseTab(Tab tab, boolean animate) {
if (!isCurrentTabInGroup()) {
hideTabStrip();
}
if (isTabStripShowing()) {
mUiUpdater.updateTabGroupPopUi();
}
}
@Override
public void didAddTab(Tab tab, int type, @TabCreationState int creationState) {
if (isTabStripShowing()) {
mUiUpdater.updateTabGroupPopUi();
return;
}
if (type != TabLaunchType.FROM_RESTORE) {
maybeShowTabStrip();
}
}
@Override
public void tabClosureUndone(Tab tab) {
if (isTabStripShowing()) {
mUiUpdater.updateTabGroupPopUi();
return;
}
maybeShowTabStrip();
}
@Override
public void restoreCompleted() {
maybeShowTabStrip();
}
};
mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(mTabModelObserver);
mOverviewModeObserver = new EmptyOverviewModeObserver() {
@Override
public void onOverviewModeStartedShowing(boolean showToolbar) {
mIsOverviewModeVisible = true;
hideTabStrip();
}
@Override
public void onOverviewModeFinishedHiding() {
mIsOverviewModeVisible = false;
maybeShowTabStrip();
}
};
overviewModeBehaviorSupplier.onAvailable(
mCallbackController.makeCancelable((overviewModeBehavior) -> {
mOverviewModeBehavior = overviewModeBehavior;
mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
}));
mKeyboardVisibilityListener = new KeyboardVisibilityDelegate.KeyboardVisibilityListener() {
private boolean mWasShowingStrip;
@Override
public void keyboardVisibilityChanged(boolean isShowing) {
if (isShowing) {
mWasShowingStrip = isTabStripShowing();
hideTabStrip();
} else {
if (mWasShowingStrip) {
maybeShowTabStrip();
}
}
}
};
KeyboardVisibilityDelegate.getInstance().addKeyboardVisibilityListener(
mKeyboardVisibilityListener);
mBottomSheetObserver = new EmptyBottomSheetObserver() {
private Boolean mWasShowingStrip;
@Override
public void onSheetStateChanged(int newState) {
if (newState == BottomSheetController.SheetState.HIDDEN) {
if (mWasShowingStrip != null && mWasShowingStrip) {
maybeShowTabStrip();
}
mWasShowingStrip = null;
} else {
if (mWasShowingStrip == null) {
mWasShowingStrip = isTabStripShowing();
}
hideTabStrip();
}
}
};
mBottomSheetController.addObserver(mBottomSheetObserver);
// TODO(yuezhanggg): Reset the strip with empty tab list as well.
mTabGroupUiController.setupLeftButtonOnClickListener(view -> hideTabStrip());
}
void onAnchorViewChanged(View anchorView, int anchorViewId) {
boolean isShowing = isTabStripShowing();
if (isShowing) {
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
}
// When showing bottom toolbar, the arrow on dismiss button should point down; when showing
// adaptive toolbar, the arrow on dismiss button should point up.
mTabGroupUiController.setupLeftButtonDrawable(anchorViewId == R.id.toolbar
? R.drawable.ic_expand_less_black_24dp
: R.drawable.ic_expand_more_black_24dp);
mModel.set(TabGroupPopupUiProperties.ANCHOR_VIEW, anchorView);
if (isShowing) {
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
}
}
void maybeShowTabStrip() {
if (mIsOverviewModeVisible || !isCurrentTabInGroup()) return;
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
}
private void hideTabStrip() {
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
}
private boolean isCurrentTabInGroup() {
assert mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()
instanceof TabGroupModelFilter;
TabGroupModelFilter filter =
(TabGroupModelFilter) mTabModelSelector.getTabModelFilterProvider()
.getCurrentTabModelFilter();
Tab currentTab = mTabModelSelector.getCurrentTab();
if (currentTab == null) return false;
List<Tab> tabgroup = filter.getRelatedTabList(currentTab.getId());
return tabgroup.size() > 1;
}
private boolean isTabStripShowing() {
return mModel.get(TabGroupPopupUiProperties.IS_VISIBLE);
}
/**
* Destroy any members that needs clean up.
*/
public void destroy() {
KeyboardVisibilityDelegate.getInstance().removeKeyboardVisibilityListener(
mKeyboardVisibilityListener);
if (mOverviewModeBehavior != null) {
mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
}
if (mCallbackController != null) {
mCallbackController.destroy();
mCallbackController = null;
}
mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver(
mTabModelObserver);
mBrowserControlsStateProvider.removeObserver(mBrowserControlsObserver);
mBottomSheetController.removeObserver(mBottomSheetObserver);
}
@VisibleForTesting
boolean getIsOverviewModeVisibleForTesting() {
return mIsOverviewModeVisible;
}
}
// Copyright 2019 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.tasks.tab_management;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.widget.FrameLayout;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tab.TabImpl;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
import org.chromium.chrome.browser.toolbar.top.ToolbarPhone;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Tests for {@link TabGroupPopupUiMediator}.
*/
@SuppressWarnings({"ResultOfMethodCallIgnored", "ArraysAsListWithZeroOrOneArgument"})
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class TabGroupPopupUiMediatorUnitTest {
@Rule
public TestRule mProcessor = new Features.JUnitProcessor();
private static final String TAB1_TITLE = "Tab1";
private static final String TAB2_TITLE = "Tab2";
private static final String TAB3_TITLE = "Tab3";
private static final String TAB4_TITLE = "Tab4";
private static final int TAB1_ID = 456;
private static final int TAB2_ID = 789;
private static final int TAB3_ID = 123;
private static final int TAB4_ID = 357;
@Mock
TabModelSelectorImpl mTabModelSelector;
@Mock
OverviewModeBehavior mOverviewModeBehavior;
@Mock
BrowserControlsStateProvider mBrowserControlsStateProvider;
@Mock
TabGroupPopupUiMediator.TabGroupPopUiUpdater mUpdater;
@Mock
TabGroupUiMediator.TabGroupUiController mTabGroupUiController;
@Mock
TabModelFilterProvider mTabModelFilterProvider;
@Mock
TabGroupModelFilter mTabGroupModelFilter;
@Mock
KeyboardVisibilityDelegate mKeyboardVisibilityDelegate;
@Mock
ToolbarPhone mTopAnchorView;
@Mock
FrameLayout mBottomAnchorView;
@Mock
BottomSheetController mBottomSheetController;
@Captor
ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
@Captor
private ArgumentCaptor<BrowserControlsStateProvider.Observer>
mBrowserControlsStateProviderObserverCaptor;
@Captor
ArgumentCaptor<OverviewModeBehavior.OverviewModeObserver> mOverviewModeObserverCaptor;
@Captor
ArgumentCaptor<KeyboardVisibilityDelegate.KeyboardVisibilityListener>
mKeyboardVisibilityListenerCaptor;
@Captor
ArgumentCaptor<BottomSheetObserver> mBottomSheetObserver;
private TabImpl mTab1;
private TabImpl mTab2;
private TabImpl mTab3;
private PropertyModel mModel;
private TabGroupPopupUiMediator mMediator;
private OneshotSupplierImpl<OverviewModeBehavior> mOverviewModeBehaviorSupplier =
new OneshotSupplierImpl<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTab1 = prepareTab(TAB1_ID, TAB1_TITLE);
mTab2 = prepareTab(TAB2_ID, TAB2_TITLE);
mTab3 = prepareTab(TAB3_ID, TAB3_TITLE);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
doReturn(mTabGroupModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
doNothing()
.when(mTabModelFilterProvider)
.addTabModelFilterObserver(mTabModelObserverCaptor.capture());
doNothing()
.when(mBrowserControlsStateProvider)
.addObserver(mBrowserControlsStateProviderObserverCaptor.capture());
doNothing()
.when(mOverviewModeBehavior)
.addOverviewModeObserver(mOverviewModeObserverCaptor.capture());
doNothing()
.when(mKeyboardVisibilityDelegate)
.addKeyboardVisibilityListener(mKeyboardVisibilityListenerCaptor.capture());
doNothing().when(mBottomSheetController).addObserver(mBottomSheetObserver.capture());
mOverviewModeBehaviorSupplier.set(mOverviewModeBehavior);
KeyboardVisibilityDelegate.setInstance(mKeyboardVisibilityDelegate);
mModel = new PropertyModel(TabGroupPopupUiProperties.ALL_KEYS);
mMediator = new TabGroupPopupUiMediator(mModel, mTabModelSelector,
mOverviewModeBehaviorSupplier, mBrowserControlsStateProvider, mUpdater,
mTabGroupUiController, mBottomSheetController);
}
@Test
public void testOnControlOffsetChanged() {
mModel.set(TabGroupPopupUiProperties.CONTENT_VIEW_ALPHA, 0f);
// Mock that the hidden ratio of browser control is 0.8765.
float hiddenRatio = 0.8765f;
doReturn(hiddenRatio).when(mBrowserControlsStateProvider).getBrowserControlHiddenRatio();
mBrowserControlsStateProviderObserverCaptor.getValue().onControlsOffsetChanged(
0, 0, 0, 0, false);
assertThat(
mModel.get(TabGroupPopupUiProperties.CONTENT_VIEW_ALPHA), equalTo(1 - hiddenRatio));
// Mock that the hidden ratio of browser control is 0.12345.
hiddenRatio = 0.1234f;
doReturn(hiddenRatio).when(mBrowserControlsStateProvider).getBrowserControlHiddenRatio();
mBrowserControlsStateProviderObserverCaptor.getValue().onControlsOffsetChanged(
0, 0, 0, 0, false);
assertThat(
mModel.get(TabGroupPopupUiProperties.CONTENT_VIEW_ALPHA), equalTo(1 - hiddenRatio));
}
@Test
public void tabSelection_Show() {
// Mock that the strip is hidden.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 and tab2 are in the same group, and tab 3 is a single tab.
createTabGroup(new ArrayList<>(Arrays.asList(mTab1, mTab2)), TAB1_ID);
createTabGroup(new ArrayList<>(Arrays.asList(mTab3)), TAB3_ID);
doReturn(mTab2).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().didSelectTab(
mTab2, TabLaunchType.FROM_CHROME_UI, TAB3_ID);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabSelection_Hide() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1 and tab2 are in the same group, and tab 3 is a single tab.
createTabGroup(new ArrayList<>(Arrays.asList(mTab1, mTab2)), TAB1_ID);
createTabGroup(new ArrayList<>(Arrays.asList(mTab3)), TAB3_ID);
doReturn(mTab3).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().didSelectTab(
mTab3, TabLaunchType.FROM_CHROME_UI, TAB1_ID);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabSelection_Update() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1 and tab2 are in the same group, tab3 and new tab are in the same group.
createTabGroup(new ArrayList<>(Arrays.asList(mTab1, mTab2)), TAB1_ID);
createTabGroup(
new ArrayList<>(Arrays.asList(mTab3, prepareTab(TAB4_ID, TAB4_TITLE))), TAB3_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().didSelectTab(
mTab1, TabLaunchType.FROM_CHROME_UI, TAB3_ID);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater).updateTabGroupPopUi();
}
@Test
public void tabSelection_SameGroup() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1 and tab2 are in the same group.
createTabGroup(new ArrayList<>(Arrays.asList(mTab1, mTab2)), TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().didSelectTab(
mTab1, TabLaunchType.FROM_CHROME_UI, TAB2_ID);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabClosure_Hide() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1 and tab2 are in the same group, and tab1 is closing.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab2));
doReturn(tabGroup).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
doReturn(tabGroup).when(mTabGroupModelFilter).getRelatedTabList(TAB2_ID);
mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabClosure_Update() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1, tab2 and tab3 are in the same group, and tab1 is closing.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab2, mTab3));
doReturn(tabGroup).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID);
doReturn(tabGroup).when(mTabGroupModelFilter).getRelatedTabList(TAB2_ID);
doReturn(tabGroup).when(mTabGroupModelFilter).getRelatedTabList(TAB3_ID);
mTabModelObserverCaptor.getValue().willCloseTab(mTab1, false);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater).updateTabGroupPopUi();
}
@Test
public void tabAddition_Show() {
// Mock that the strip is hidden.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 and tab2 are in the same group, and tab2 has just been created.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().didAddTab(
mTab2, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabAddition_Update() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1, tab2 and tab3 are in the same group, and tab3 has just been created.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().didAddTab(
mTab3, TabLaunchType.FROM_CHROME_UI, TabCreationState.LIVE_IN_FOREGROUND);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater).updateTabGroupPopUi();
}
@Test
public void tabAddition_NotShow_Restore() {
// Mock that the strip is hidden.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 and tab2 are in the same group, and they are being restored.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().didAddTab(
mTab2, TabLaunchType.FROM_RESTORE, TabCreationState.FROZEN_ON_RESTORE);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabClosureUndone_Show() {
// Mock that the strip is hiding.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 and tab2 are in the same group, and we have just undone the closure of
// tab2.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().tabClosureUndone(mTab2);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabClosureUndone_Update() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1, tab2 and tab3 are in the same group, and we have just undone the closure
// of tab3.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2, mTab3));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().tabClosureUndone(mTab3);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
verify(mUpdater).updateTabGroupPopUi();
}
@Test
public void tabClosureUndone_NotShow() {
// Mock that the strip is hiding.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 is a single tab.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1));
createTabGroup(tabGroup, TAB1_ID);
mTabModelObserverCaptor.getValue().tabClosureUndone(mTab1);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
verify(mUpdater, never()).updateTabGroupPopUi();
}
@Test
public void tabRestoreCompletion_NotShow() {
// Mock that the strip is hiding.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 is the current tab and it is a single tab.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().restoreCompleted();
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void tabRestoreCompletion_Show() {
// Mock that the strip is hiding.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 is the current tab and it is in a group of {tab1, tab2}.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mTabModelObserverCaptor.getValue().restoreCompleted();
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
}
@Test
public void testOverviewModeHiding() {
// Mock that the strip is hiding.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 and tab2 are in the same group, and tab1 is the current tab.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mOverviewModeObserverCaptor.getValue().onOverviewModeFinishedHiding();
assertThat(mMediator.getIsOverviewModeVisibleForTesting(), equalTo(false));
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
}
@Test
public void testOverviewModeShowing() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Mock that tab1 and tab2 are in the same group, and tab1 is the current tab.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
mOverviewModeObserverCaptor.getValue().onOverviewModeStartedShowing(true);
assertThat(mMediator.getIsOverviewModeVisibleForTesting(), equalTo(true));
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testNeverShowStripWhenOverviewVisible() {
// Mock that the strip is hiding and overview is visible.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
mOverviewModeObserverCaptor.getValue().onOverviewModeStartedShowing(true);
assertThat(mMediator.getIsOverviewModeVisibleForTesting(), equalTo(true));
// Calling maybeShowTabStrip should never show strip in this case.
mMediator.maybeShowTabStrip();
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testNeverShowStripWhenSingleTab() {
// Mock that the strip is hiding and overview is visible.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
// Mock that tab1 is the current tab and it is a single tab.
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
// Calling maybeShowTabStrip should never show strip in this case.
mMediator.maybeShowTabStrip();
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testShowKeyboard_HideStrip() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
mKeyboardVisibilityListenerCaptor.getValue().keyboardVisibilityChanged(true);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testHideKeyboard_ShowStrip() {
// Mock that the strip is showing before showing the keyboard. tab1 and tab2 are in the same
// group, and tab1 is the current tab.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
// Hide the keyboard after showing it.
mKeyboardVisibilityListenerCaptor.getValue().keyboardVisibilityChanged(true);
mKeyboardVisibilityListenerCaptor.getValue().keyboardVisibilityChanged(false);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
}
@Test
public void testHideKeyboard_NotReshowStrip() {
// Mock that the strip is hidden before showing the keyboard. tab1 and tab2 are in the same
// group, and tab1 is the current tab.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
// Hide the keyboard after showing it.
mKeyboardVisibilityListenerCaptor.getValue().keyboardVisibilityChanged(true);
mKeyboardVisibilityListenerCaptor.getValue().keyboardVisibilityChanged(false);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testShowBottomSheet_HideStrip() {
// Mock that the strip is showing.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
// Show bottom sheet.
mBottomSheetObserver.getValue().onSheetStateChanged(BottomSheetController.SheetState.PEEK);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testHideBottomSheet_ShowStrip() {
// Mock that the strip is showing before showing the bottom sheet. tab1 and tab2 are in the
// same group, and tab1 is the current tab.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
// Hide the bottom sheet after showing it.
mBottomSheetObserver.getValue().onSheetStateChanged(BottomSheetController.SheetState.PEEK);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
mBottomSheetObserver.getValue().onSheetStateChanged(
BottomSheetController.SheetState.HIDDEN);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(true));
}
@Test
public void testHideBottomSheet_NotReshowStrip() {
// Mock that the strip is hidden before showing the bottom sheet. tab1 and tab2 are in the
// same group, and tab1 is the current tab.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, false);
List<Tab> tabGroup = new ArrayList<>(Arrays.asList(mTab1, mTab2));
createTabGroup(tabGroup, TAB1_ID);
doReturn(mTab1).when(mTabModelSelector).getCurrentTab();
// Hide the bottom sheet after showing it.
mBottomSheetObserver.getValue().onSheetStateChanged(BottomSheetController.SheetState.PEEK);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
mBottomSheetObserver.getValue().onSheetStateChanged(
BottomSheetController.SheetState.HIDDEN);
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testAnchorViewChange_TopToolbar() {
mMediator.onAnchorViewChanged(mTopAnchorView, R.id.toolbar);
assertThat(mModel.get(TabGroupPopupUiProperties.ANCHOR_VIEW), equalTo(mTopAnchorView));
verify(mTabGroupUiController)
.setupLeftButtonDrawable(eq(R.drawable.ic_expand_less_black_24dp));
}
@Test
public void testAnchorViewChange_BottomToolbar() {
mMediator.onAnchorViewChanged(mBottomAnchorView, R.id.bottom_controls);
assertThat(mModel.get(TabGroupPopupUiProperties.ANCHOR_VIEW), equalTo(mBottomAnchorView));
verify(mTabGroupUiController)
.setupLeftButtonDrawable(eq(R.drawable.ic_expand_more_black_24dp));
}
@Test
public void testAnchorViewChange_WithStripShowing() {
// Mock that strip is showing when anchor view changes.
mModel.set(TabGroupPopupUiProperties.IS_VISIBLE, true);
mMediator.onAnchorViewChanged(mBottomAnchorView, R.id.bottom_controls);
assertTrue(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE));
assertThat(mModel.get(TabGroupPopupUiProperties.ANCHOR_VIEW), equalTo(mBottomAnchorView));
verify(mTabGroupUiController)
.setupLeftButtonDrawable(eq(R.drawable.ic_expand_more_black_24dp));
}
@Test
public void testNoCurrentTab_NotShow() {
// Mock overview mode is hiding, and current tab is null.
doReturn(null).when(mTabModelSelector).getCurrentTab();
mOverviewModeObserverCaptor.getValue().onOverviewModeFinishedHiding();
assertThat(mMediator.getIsOverviewModeVisibleForTesting(), equalTo(false));
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
mMediator.maybeShowTabStrip();
assertThat(mModel.get(TabGroupPopupUiProperties.IS_VISIBLE), equalTo(false));
}
@Test
public void testDestroy() {
mMediator.destroy();
verify(mKeyboardVisibilityDelegate)
.removeKeyboardVisibilityListener(mKeyboardVisibilityListenerCaptor.capture());
verify(mOverviewModeBehavior)
.removeOverviewModeObserver(mOverviewModeObserverCaptor.capture());
verify(mTabModelFilterProvider)
.removeTabModelFilterObserver(mTabModelObserverCaptor.capture());
verify(mBrowserControlsStateProvider)
.removeObserver(mBrowserControlsStateProviderObserverCaptor.capture());
}
// TODO(yuezhanggg): Pull methods below to a utility class.
private TabImpl prepareTab(int id, String title) {
TabImpl tab = TabUiUnitTestUtils.prepareTab(id, title, "");
doReturn(true).when(tab).isIncognito();
return tab;
}
private void createTabGroup(List<Tab> tabs, int rootId) {
for (Tab tab : tabs) {
when(mTabGroupModelFilter.getRelatedTabList(tab.getId())).thenReturn(tabs);
CriticalPersistedTabData criticalPersistedTabData = CriticalPersistedTabData.from(tab);
doReturn(rootId).when(criticalPersistedTabData).getRootId();
}
}
}
// Copyright 2018 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.toolbar.bottom;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewStub;
import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.supplier.OneShotCallback;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ThemeColorProvider;
import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.omnibox.LocationBar;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
import org.chromium.chrome.browser.toolbar.TabCountProvider;
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.feature_engagement.Tracker;
/**
* The root coordinator for the bottom toolbar. It has two sub-components: the browsing mode bottom
* toolbar and the tab switcher mode bottom toolbar.
*/
class BottomToolbarCoordinator {
/** The browsing mode bottom toolbar component */
private final BrowsingModeBottomToolbarCoordinator mBrowsingModeCoordinator;
/** The tab switcher mode bottom toolbar component */
private TabSwitcherBottomToolbarCoordinator mTabSwitcherModeCoordinator;
/** The tab switcher mode bottom toolbar stub that will be inflated when native is ready. */
private final ViewStub mTabSwitcherModeStub;
/** A provider that notifies components when the theme color changes.*/
private final ThemeColorProvider mThemeColorProvider;
/** The overview mode manager. */
private OverviewModeBehavior mOverviewModeBehavior;
private OverviewModeObserver mOverviewModeObserver;
/** The activity tab provider. */
private ActivityTabProvider mTabProvider;
private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
private final Callback<ShareDelegate> mShareDelegateSupplierCallback;
private ObservableSupplierImpl<OnClickListener> mShareButtonListenerSupplier =
new ObservableSupplierImpl<>();
private final Supplier<Boolean> mShowStartSurfaceCallable;
private AppMenuButtonHelper mMenuButtonHelper;
private OneshotSupplier<OverviewModeBehavior> mOverviewModeBehaviorSupplier;
/**
* Build the coordinator that manages the bottom toolbar.
* @param stub The bottom toolbar {@link ViewStub} to inflate.
* @param tabProvider The {@link ActivityTabProvider} used for making the IPH.
* @param themeColorProvider The {@link ThemeColorProvider} for the bottom toolbar.
* @param shareDelegateSupplier The supplier for the {@link ShareDelegate} the bottom controls
* should use to share content.
* @param showStartSurfaceCallable The action that opens the start surface, returning true if
* the start surface is shown.
* @param openHomepageAction The action that opens the homepage.
* @param setUrlBarFocusAction The function that sets Url bar focus. The first argument is
* @param overviewModeBehaviorSupplier Supplier for the overview mode manager.
* @param menuButtonHelperSupplier
*/
BottomToolbarCoordinator(ViewStub stub, ActivityTabProvider tabProvider,
OnLongClickListener tabsSwitcherLongClickListner, ThemeColorProvider themeColorProvider,
ObservableSupplier<ShareDelegate> shareDelegateSupplier,
Supplier<Boolean> showStartSurfaceCallable, Runnable openHomepageAction,
Callback<Integer> setUrlBarFocusAction,
ObservableSupplier<AppMenuButtonHelper> menuButtonHelperSupplier,
OneshotSupplier<OverviewModeBehavior> overviewModeBehaviorSupplier) {
View root = stub.inflate();
mOverviewModeBehaviorSupplier = overviewModeBehaviorSupplier;
mShowStartSurfaceCallable = showStartSurfaceCallable;
final OnClickListener homeButtonListener = v -> {
recordBottomToolbarUseForIPH();
openHomepageAction.run();
};
final OnClickListener searchAcceleratorListener = v -> {
recordBottomToolbarUseForIPH();
RecordUserAction.record("MobileToolbarOmniboxAcceleratorTap");
// Only switch to HomePage when overview is showing.
if (mOverviewModeBehavior != null && mOverviewModeBehavior.overviewVisible()) {
mShowStartSurfaceCallable.get();
}
setUrlBarFocusAction.onResult(LocationBar.OmniboxFocusReason.ACCELERATOR_TAP);
};
mBrowsingModeCoordinator = new BrowsingModeBottomToolbarCoordinator(root, tabProvider,
homeButtonListener, searchAcceleratorListener, mShareButtonListenerSupplier,
tabsSwitcherLongClickListner, mOverviewModeBehaviorSupplier);
mTabSwitcherModeStub = root.findViewById(R.id.bottom_toolbar_tab_switcher_mode_stub);
mThemeColorProvider = themeColorProvider;
mTabProvider = tabProvider;
mShareDelegateSupplier = shareDelegateSupplier;
mShareDelegateSupplierCallback = this::onShareDelegateAvailable;
mShareDelegateSupplier.addObserver(mShareDelegateSupplierCallback);
new OneShotCallback<>(menuButtonHelperSupplier, (menuButtonHelper) -> {
if (menuButtonHelper != null) {
mMenuButtonHelper = menuButtonHelper;
mMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH());
}
});
}
/**
* Initialize the bottom toolbar with the components that had native initialization
* dependencies.
* <p>
* Calling this must occur after the native library have completely loaded.
* @param tabSwitcherListener An {@link OnClickListener} that is triggered when the
* tab switcher button is clicked.
* @param newTabClickListener An {@link OnClickListener} that is triggered when the
* new tab button is clicked.
* @param tabCountProvider Updates the tab count number in the tab switcher button and in the
* incognito toggle tab layout.
* @param incognitoStateProvider Notifies components when incognito mode is entered or exited.
* @param topToolbarRoot The root {@link ViewGroup} of the top toolbar.
* @param closeAllTabsAction The runnable that closes all tabs in the current tab model.
*/
void initializeWithNative(OnClickListener tabSwitcherListener,
OnClickListener newTabClickListener, TabCountProvider tabCountProvider,
IncognitoStateProvider incognitoStateProvider, ViewGroup topToolbarRoot,
Runnable closeAllTabsAction) {
final OnClickListener closeTabsClickListener = v -> {
recordBottomToolbarUseForIPH();
final boolean isIncognito = incognitoStateProvider.isIncognitoSelected();
if (isIncognito) {
RecordUserAction.record("MobileToolbarCloseAllIncognitoTabsButtonTap");
} else {
RecordUserAction.record("MobileToolbarCloseAllRegularTabsButtonTap");
}
closeAllTabsAction.run();
};
newTabClickListener = wrapBottomToolbarClickListenerForIPH(newTabClickListener);
tabSwitcherListener = wrapBottomToolbarClickListenerForIPH(tabSwitcherListener);
mBrowsingModeCoordinator.initializeWithNative(newTabClickListener, tabSwitcherListener,
mMenuButtonHelper, tabCountProvider, mThemeColorProvider, incognitoStateProvider);
mTabSwitcherModeCoordinator = new TabSwitcherBottomToolbarCoordinator(mTabSwitcherModeStub,
topToolbarRoot, incognitoStateProvider, mThemeColorProvider, newTabClickListener,
closeTabsClickListener, mMenuButtonHelper, tabCountProvider);
// Do not change bottom bar if StartSurface Single Pane is enabled and HomePage is not
// customized.
if (!ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()
&& BottomToolbarVariationManager.shouldBottomToolbarBeVisibleInOverviewMode()) {
mOverviewModeObserver = new EmptyOverviewModeObserver() {
@Override
public void onOverviewModeStartedShowing(boolean showToolbar) {
mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(false);
if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
mBrowsingModeCoordinator.getShareButton().setEnabled(false);
}
if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
mBrowsingModeCoordinator.getHomeButton().setEnabled(false);
}
}
@Override
public void onOverviewModeStartedHiding(
boolean showToolbar, boolean delayAnimation) {
mBrowsingModeCoordinator.getSearchAccelerator().setEnabled(true);
if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
mBrowsingModeCoordinator.getShareButton().updateButtonEnabledState(
mTabProvider.get());
}
if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
mBrowsingModeCoordinator.getHomeButton().updateButtonEnabledState(
mTabProvider.get());
}
}
};
mOverviewModeBehaviorSupplier.onAvailable(this::setOverviewModeBehavior);
}
}
/**
* @param isVisible Whether the bottom toolbar is visible.
*/
void setBottomToolbarVisible(boolean isVisible) {
if (mTabSwitcherModeCoordinator != null) {
mTabSwitcherModeCoordinator.showToolbarOnTop(!isVisible);
}
mBrowsingModeCoordinator.onVisibilityChanged(isVisible);
}
/**
* Clean up any state when the bottom toolbar is destroyed.
*/
void destroy() {
mBrowsingModeCoordinator.destroy();
if (mTabSwitcherModeCoordinator != null) {
mTabSwitcherModeCoordinator.destroy();
mTabSwitcherModeCoordinator = null;
}
if (mOverviewModeBehavior != null) {
mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver);
mOverviewModeBehavior = null;
}
if (mOverviewModeBehaviorSupplier != null) {
mOverviewModeBehaviorSupplier = null;
}
mThemeColorProvider.destroy();
mShareDelegateSupplier.removeObserver(mShareDelegateSupplierCallback);
}
private void onShareDelegateAvailable(ShareDelegate shareDelegate) {
final OnClickListener shareButtonListener = v -> {
if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
recordBottomToolbarUseForIPH();
RecordUserAction.record("MobileBottomToolbarShareButton");
}
Tab tab = mTabProvider.get();
shareDelegate.share(tab, /*shareDirectly=*/false);
};
mShareButtonListenerSupplier.set(shareButtonListener);
}
private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
assert overviewModeBehavior != null;
assert mOverviewModeBehavior
== null
: "TODO(https://crbug.com/1084528): the overview mode manager should set at most once.";
mOverviewModeBehavior = overviewModeBehavior;
mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver);
}
/** Record that the bottom toolbar was used for IPH reasons. */
private void recordBottomToolbarUseForIPH() {
Tab tab = mTabProvider.get();
if (tab == null) return;
Tracker tracker =
TrackerFactory.getTrackerForProfile(Profile.fromWebContents(tab.getWebContents()));
tracker.notifyEvent(EventConstants.CHROME_DUET_USED_BOTTOM_TOOLBAR);
}
/**
* Add bottom toolbar IPH tracking to an existing click listener.
* @param listener The listener to add bottom toolbar tracking to.
*/
private OnClickListener wrapBottomToolbarClickListenerForIPH(OnClickListener listener) {
return (v) -> {
recordBottomToolbarUseForIPH();
listener.onClick(v);
};
}
}
// Copyright 2018 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.toolbar.bottom;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import org.chromium.base.Callback;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ThemeColorProvider;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabUtils;
import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
import org.chromium.chrome.browser.toolbar.HomeButton;
import org.chromium.chrome.browser.toolbar.IncognitoStateProvider;
import org.chromium.chrome.browser.toolbar.TabCountProvider;
import org.chromium.chrome.browser.toolbar.TabSwitcherButtonCoordinator;
import org.chromium.chrome.browser.toolbar.TabSwitcherButtonView;
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
/**
* The coordinator for the browsing mode bottom toolbar. This class has two primary components,
* an Android view that handles user actions and a composited texture that draws when the controls
* are being scrolled off-screen. The Android version does not draw unless the controls offset is 0.
*/
public class BrowsingModeBottomToolbarCoordinator {
/** The mediator that handles events from outside the browsing mode bottom toolbar. */
private final BrowsingModeBottomToolbarMediator mMediator;
/** The home button that lives in the bottom toolbar. */
private final HomeButton mHomeButton;
/** The share button that lives in the bottom toolbar. */
private final ShareButton mShareButton;
/** The new tab button that lives in the bottom toolbar. */
private final BottomToolbarNewTabButton mNewTabButton;
/** The search accelerator that lives in the bottom toolbar. */
private final SearchAccelerator mSearchAccelerator;
/** The tab switcher button component that lives in the bottom toolbar. */
private final TabSwitcherButtonCoordinator mTabSwitcherButtonCoordinator;
/** The tab switcher button view that lives in the bottom toolbar. */
private final TabSwitcherButtonView mTabSwitcherButtonView;
/** The view group that includes all views shown on browsing mode */
private final BrowsingModeBottomToolbarLinearLayout mToolbarRoot;
/** The model for the browsing mode bottom toolbar that holds all of its state. */
private final BrowsingModeBottomToolbarModel mModel;
/** The callback to be exectured when the share button on click listener is available. */
private Callback<OnClickListener> mShareButtonListenerSupplierCallback;
/** The supplier for the share button on click listener. */
private ObservableSupplier<OnClickListener> mShareButtonListenerSupplier;
/** The activity tab provider that used for making the IPH. */
private final ActivityTabProvider mTabProvider;
/**
* Build the coordinator that manages the browsing mode bottom toolbar.
* @param root The root {@link View} for locating the views to inflate.
* @param tabProvider The {@link ActivityTabProvider} used for making the IPH.
* @param homeButtonListener The {@link OnClickListener} for the home button.
* @param searchAcceleratorListener The {@link OnClickListener} for the search accelerator.
* @param shareButtonListener The {@link OnClickListener} for the share button.
* @param overviewModeBehaviorSupplier Supplier for the overview mode manager.
*/
BrowsingModeBottomToolbarCoordinator(View root, ActivityTabProvider tabProvider,
OnClickListener homeButtonListener, OnClickListener searchAcceleratorListener,
ObservableSupplier<OnClickListener> shareButtonListenerSupplier,
OnLongClickListener tabSwitcherLongClickListener,
OneshotSupplier<OverviewModeBehavior> overviewModeBehaviorSupplier) {
mModel = new BrowsingModeBottomToolbarModel();
mToolbarRoot = root.findViewById(R.id.bottom_toolbar_browsing);
mTabProvider = tabProvider;
PropertyModelChangeProcessor.create(
mModel, mToolbarRoot, new BrowsingModeBottomToolbarViewBinder());
mMediator = new BrowsingModeBottomToolbarMediator(mModel);
mHomeButton = mToolbarRoot.findViewById(R.id.bottom_home_button);
mHomeButton.setOnClickListener(homeButtonListener);
mHomeButton.setActivityTabProvider(mTabProvider);
mNewTabButton = mToolbarRoot.findViewById(R.id.bottom_new_tab_button);
mShareButton = mToolbarRoot.findViewById(R.id.bottom_share_button);
mSearchAccelerator = mToolbarRoot.findViewById(R.id.search_accelerator);
mSearchAccelerator.setOnClickListener(searchAcceleratorListener);
// TODO(amaralp): Make this adhere to MVC framework.
mTabSwitcherButtonView = mToolbarRoot.findViewById(R.id.bottom_tab_switcher_button);
mTabSwitcherButtonCoordinator = new TabSwitcherButtonCoordinator(mTabSwitcherButtonView);
mTabSwitcherButtonView.setOnLongClickListener(tabSwitcherLongClickListener);
if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) {
mNewTabButton.setVisibility(View.VISIBLE);
}
if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
mHomeButton.setVisibility(View.VISIBLE);
}
if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) {
mTabSwitcherButtonView.setVisibility(View.VISIBLE);
}
if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
mShareButton.setVisibility(View.VISIBLE);
mShareButtonListenerSupplierCallback = shareButtonListener -> {
mShareButton.setOnClickListener(shareButtonListener);
};
mShareButtonListenerSupplier = shareButtonListenerSupplier;
mShareButton.setActivityTabProvider(mTabProvider);
mShareButtonListenerSupplier.addObserver(mShareButtonListenerSupplierCallback);
}
overviewModeBehaviorSupplier.onAvailable(this::setOverviewModeBehavior);
}
/**
* @param isVisible Whether the bottom toolbar is visible.
*/
void onVisibilityChanged(boolean isVisible) {
if (isVisible) return;
Tab tab = mTabProvider.get();
if (tab != null) mMediator.dismissIPH(TabUtils.getActivity(tab));
}
/**
* Initialize the bottom toolbar with the components that had native initialization
* dependencies.
* <p>
* Calling this must occur after the native library have completely loaded.
* @param tabSwitcherListener An {@link OnClickListener} that is triggered when the
* tab switcher button is clicked.
* @param menuButtonHelper An {@link AppMenuButtonHelper} that is triggered when the
* menu button is clicked.
* @param tabCountProvider Updates the tab count number in the tab switcher button.
* @param themeColorProvider Notifies components when theme color changes.
* @param incognitoStateProvider Notifies components when incognito state changes.
*/
void initializeWithNative(OnClickListener newTabListener, OnClickListener tabSwitcherListener,
AppMenuButtonHelper menuButtonHelper, TabCountProvider tabCountProvider,
ThemeColorProvider themeColorProvider, IncognitoStateProvider incognitoStateProvider) {
mMediator.setThemeColorProvider(themeColorProvider);
if (BottomToolbarVariationManager.isNewTabButtonOnBottom()) {
mNewTabButton.setOnClickListener(newTabListener);
mNewTabButton.setThemeColorProvider(themeColorProvider);
mNewTabButton.setIncognitoStateProvider(incognitoStateProvider);
}
if (BottomToolbarVariationManager.isHomeButtonOnBottom()) {
mHomeButton.setThemeColorProvider(themeColorProvider);
}
if (BottomToolbarVariationManager.isShareButtonOnBottom()) {
mShareButton.setThemeColorProvider(themeColorProvider);
}
mSearchAccelerator.setThemeColorProvider(themeColorProvider);
mSearchAccelerator.setIncognitoStateProvider(incognitoStateProvider);
if (BottomToolbarVariationManager.isTabSwitcherOnBottom()) {
mTabSwitcherButtonCoordinator.setTabSwitcherListener(tabSwitcherListener);
mTabSwitcherButtonCoordinator.setThemeColorProvider(themeColorProvider);
mTabSwitcherButtonCoordinator.setTabCountProvider(tabCountProvider);
}
}
private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
assert overviewModeBehavior != null;
// If StartSurface is HomePage, BrowsingModeBottomToolbar is shown in browsing mode and in
// overview mode. We need to pass the OverviewModeBehavior to the buttons so they are
// disabled based on the overview state.
if (ReturnToChromeExperimentsUtil.shouldShowStartSurfaceAsTheHomePage()) {
mShareButton.setOverviewModeBehavior(overviewModeBehavior);
mTabSwitcherButtonCoordinator.setOverviewModeBehavior(overviewModeBehavior);
mHomeButton.setOverviewModeBehavior(overviewModeBehavior);
}
}
/**
* @param enabled Whether to disable click events on the bottom toolbar. Setting true can also
* prevent from all click events on toolbar and all children views on toolbar.
*/
void setTouchEnabled(boolean enabled) {
mToolbarRoot.setTouchEnabled(enabled);
}
/**
* @param visible Whether to hide the tab switcher bottom toolbar
*/
void setVisible(boolean visible) {
mModel.set(BrowsingModeBottomToolbarModel.IS_VISIBLE, visible);
}
/**
* @return The browsing mode bottom toolbar's share button.
*/
ShareButton getShareButton() {
return mShareButton;
}
/**
* @return The browsing mode bottom toolbar's tab switcher button.
*/
TabSwitcherButtonView getTabSwitcherButtonView() {
return mTabSwitcherButtonView;
}
/**
* @return The browsing mode bottom toolbar's search button.
*/
SearchAccelerator getSearchAccelerator() {
return mSearchAccelerator;
}
/**
* @return The browsing mode bottom toolbar's home button.
*/
HomeButton getHomeButton() {
return mHomeButton;
}
/**
* Clean up any state when the browsing mode bottom toolbar is destroyed.
*/
public void destroy() {
if (mShareButtonListenerSupplier != null) {
mShareButtonListenerSupplier.removeObserver(mShareButtonListenerSupplierCallback);
}
mMediator.destroy();
mHomeButton.destroy();
mShareButton.destroy();
mSearchAccelerator.destroy();
mTabSwitcherButtonCoordinator.destroy();
}
}
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