chromium/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java

// Copyright 2021 The Chromium Authors
// 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.feed;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.MotionEvent;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;

import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Callback;
import org.chromium.base.LocaleUtils;
import org.chromium.base.jank_tracker.PlaceholderJankTracker;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.feed.componentinterfaces.SurfaceCoordinator;
import org.chromium.chrome.browser.feed.sections.SectionHeaderListProperties;
import org.chromium.chrome.browser.feed.sections.SectionHeaderView;
import org.chromium.chrome.browser.feed.webfeed.WebFeedBridge;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
import org.chromium.chrome.browser.ntp.NewTabPageLaunchOrigin;
import org.chromium.chrome.browser.ntp.cards.SignInPromo;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.signin.services.SigninManager;
import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.xsurface.HybridListRenderer;
import org.chromium.chrome.browser.xsurface.ProcessScope;
import org.chromium.chrome.browser.xsurface.feed.FeedLaunchReliabilityLogger;
import org.chromium.chrome.browser.xsurface.feed.FeedLaunchReliabilityLogger.SurfaceType;
import org.chromium.chrome.browser.xsurface.feed.FeedSurfaceScope;
import org.chromium.chrome.browser.xsurface.feed.FeedSurfaceScopeDependencyProvider;
import org.chromium.chrome.browser.xsurface_provider.XSurfaceProcessScopeProvider;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.components.feed.proto.wire.ReliabilityLoggingEnums.DiscoverLaunchResult;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.search_engines.TemplateUrlService;
import org.chromium.components.signin.AccountCapabilitiesConstants;
import org.chromium.components.signin.base.AccountCapabilities;
import org.chromium.components.signin.base.AccountInfo;
import org.chromium.components.signin.base.CoreAccountId;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.components.user_prefs.UserPrefsJni;
import org.chromium.ui.base.WindowAndroid;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/** Tests for {@link FeedSurfaceCoordinator}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@DisableFeatures({
    ChromeFeatureList.WEB_FEED_SORT,
    ChromeFeatureList.WEB_FEED_ONBOARDING,
    // TODO(crbug.com/40858677): Disabling the feature explicitly, because native is not
    // available to provide a default value. This should be enabled if the feature is enabled by
    // default or removed if the flag is removed.
    ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS,
    ChromeFeatureList.FEED_CONTAINMENT
})
@EnableFeatures({
    ChromeFeatureList.KID_FRIENDLY_CONTENT_FEED,
    ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS,
})
public class FeedSurfaceCoordinatorTest {
    private static final @SurfaceType int SURFACE_TYPE = SurfaceType.NEW_TAB_PAGE;
    private static final long SURFACE_CREATION_TIME_NS = 1234L;

    private class TestLifecycleManager extends FeedSurfaceLifecycleManager {
        public TestLifecycleManager(Activity activity, FeedSurfaceCoordinator coordinator) {
            super(activity, coordinator);
        }

        @Override
        public boolean canShow() {
            return true;
        }
    }

    private class TestSurfaceDelegate implements FeedSurfaceDelegate {
        @Override
        public FeedSurfaceLifecycleManager createStreamLifecycleManager(
                Activity activity, SurfaceCoordinator coordinator, Profile profile) {
            mLifecycleManager =
                    new TestLifecycleManager(activity, (FeedSurfaceCoordinator) coordinator);
            return mLifecycleManager;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent e) {
            return false;
        }
    }

    private class TestTabModel extends EmptyTabModel {
        public ArrayList<TabModelObserver> mObservers = new ArrayList<TabModelObserver>();

        @Override
        public void addObserver(TabModelObserver observer) {
            mObservers.add(observer);
        }

        void selectTab() {
            for (TabModelObserver observer : mObservers) {
                observer.didSelectTab(null, 0, 0);
            }
        }
    }

    private TestTabModel mTabModel = new TestTabModel();
    private TestTabModel mTabModelIncognito = new TestTabModel();

    private FeedSurfaceCoordinator mCoordinator;

    @Rule public JniMocker mocker = new JniMocker();

    private Activity mActivity;
    private RecyclerView mRecyclerView;
    @Mock private LinearLayoutManager mLayoutManager;
    private TestLifecycleManager mLifecycleManager;

    // Mocked Direct dependencies.
    @Mock private SnackbarManager mSnackbarManager;
    @Mock private NativePageNavigationDelegate mPageNavigationDelegate;
    @Mock private BottomSheetController mBottomSheetController;
    @Mock private SnapScrollHelper mSnapHelper;
    @Mock private WindowAndroid mWindowAndroid;
    @Mock private Supplier<ShareDelegate> mShareDelegateSupplier;
    @Mock private SectionHeaderView mSectionHeaderView;
    @Mock private FeedActionDelegate mFeedActionDelegate;

    // Mocked JNI.
    @Mock private FeedSurfaceRendererBridge.Natives mFeedSurfaceRendererBridgeJniMock;
    @Mock private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
    @Mock private WebFeedBridge.Natives mWebFeedBridgeJniMock;
    @Mock private FeedProcessScopeDependencyProvider.Natives mProcessScopeJniMock;
    @Mock private FeedReliabilityLoggingBridge.Natives mFeedReliabilityLoggingBridgeJniMock;
    @Mock private UserPrefs.Natives mUserPrefsJniMock;

    // Mocked xSurface setup.
    @Mock private ProcessScope mProcessScope;
    @Mock private FeedSurfaceScope mSurfaceScope;
    @Mock private HybridListRenderer mRenderer;
    @Captor private ArgumentCaptor<FeedListContentManager> mContentManagerCaptor;

    // Mocked indirect dependencies.
    @Mock private Profile mProfileMock;
    @Mock private IdentityServicesProvider mIdentityService;
    @Mock private SigninManager mSigninManager;
    @Mock private IdentityManager mIdentityManager;
    @Mock private PrefChangeRegistrar mPrefChangeRegistrar;
    @Mock private PrefService mPrefService;
    @Mock private TemplateUrlService mUrlService;
    @Mock private Resources mResources;
    @Mock private RecyclerView.Adapter mAdapter;
    @Mock private FeedLaunchReliabilityLogger mLaunchReliabilityLogger;
    @Mock private PrivacyPreferencesManagerImpl mPrivacyPreferencesManager;
    @Mock private Tracker mTracker;
    @Mock private ScrollableContainerDelegate mScrollableContainerDelegate;
    @Mock ObservableSupplier<Integer> mTabStripHeightSupplier;

    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();

    private FeedSurfaceMediator mMediatorSpy;
    private int mTabStripHeight;

    @Before
    public void setUp() {
        Configuration config = new Configuration();
        config.setLocale(new Locale("en", "US"));
        LocaleUtils.setDefaultLocalesFromConfiguration(config);

        mActivity = Robolectric.buildActivity(Activity.class).get();
        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
        mocker.mock(FeedSurfaceRendererBridgeJni.TEST_HOOKS, mFeedSurfaceRendererBridgeJniMock);
        mocker.mock(FeedServiceBridgeJni.TEST_HOOKS, mFeedServiceBridgeJniMock);
        mocker.mock(WebFeedBridge.getTestHooksForTesting(), mWebFeedBridgeJniMock);
        mocker.mock(FeedProcessScopeDependencyProviderJni.TEST_HOOKS, mProcessScopeJniMock);
        mocker.mock(
                FeedReliabilityLoggingBridge.getTestHooksForTesting(),
                mFeedReliabilityLoggingBridgeJniMock);
        mocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock);

        when(mFeedServiceBridgeJniMock.getLoadMoreTriggerLookahead()).thenReturn(5);

        // Profile/identity service set up.
        ProfileManager.setLastUsedProfileForTesting(mProfileMock);
        IdentityServicesProvider.setInstanceForTests(mIdentityService);
        when(mIdentityService.getSigninManager(any(Profile.class))).thenReturn(mSigninManager);
        when(mIdentityService.getIdentityManager(any(Profile.class))).thenReturn(mIdentityManager);
        when(mSigninManager.getIdentityManager()).thenReturn(mIdentityManager);
        SignInPromo.setDisablePromoForTesting(true);

        // Preferences to enable feed.
        FeedSurfaceMediator.setPrefForTest(mPrefChangeRegistrar, mPrefService);
        FeedFeatures.setFakePrefsForTest(mPrefService);
        // We want to make the feed service bridge ignore the ablation flag.
        when(mFeedServiceBridgeJniMock.isEnabled())
                .thenAnswer(invocation -> mPrefService.getBoolean(Pref.ENABLE_SNIPPETS));
        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS)).thenReturn(true);
        when(mPrefService.getBoolean(Pref.ARTICLES_LIST_VISIBLE)).thenReturn(true);
        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
        TemplateUrlServiceFactory.setInstanceForTesting(mUrlService);
        when(mPrivacyPreferencesManager.isMetricsReportingEnabled()).thenReturn(true);
        when(mUserPrefsJniMock.get(any(Profile.class))).thenReturn(mPrefService);

        // Resources set up.
        when(mSectionHeaderView.getResources()).thenReturn(mResources);
        when(mResources.getString(anyInt())).thenReturn("Test");

        mRecyclerView = new RecyclerView(mActivity);
        mRecyclerView.setAdapter(mAdapter);

        XSurfaceProcessScopeProvider.setProcessScopeForTesting(mProcessScope);

        when(mProcessScope.obtainFeedSurfaceScope(any(FeedSurfaceScopeDependencyProvider.class)))
                .thenReturn(mSurfaceScope);
        when(mSurfaceScope.provideListRenderer()).thenReturn(mRenderer);
        when(mRenderer.bind(mContentManagerCaptor.capture(), isNull(), anyInt()))
                .thenReturn(mRecyclerView);
        when(mSurfaceScope.getLaunchReliabilityLogger()).thenReturn(mLaunchReliabilityLogger);
        TrackerFactory.setTrackerForTests(mTracker);

        ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.CREATED);

        mTabStripHeight = mActivity.getResources().getDimensionPixelSize(R.dimen.tab_strip_height);
        when(mTabStripHeightSupplier.get()).thenReturn(mTabStripHeight);

        mCoordinator = createCoordinator();

        mRecyclerView.setLayoutManager(mLayoutManager);

        mMediatorSpy = Mockito.spy(mCoordinator.getMediatorForTesting());
        mCoordinator.setMediatorForTesting(mMediatorSpy);

        // Print logs to stdout.
        ShadowLog.stream = System.out;
    }

    @After
    public void tearDown() {
        mCoordinator.destroy();
        FeedSurfaceTracker.getInstance().resetForTest();
        FeedSurfaceMediator.setPrefForTest(null, null);
    }

    @Test
    public void testInactiveInitially() {
        assertEquals(false, mCoordinator.isActive());
        assertEquals(false, hasStreamBound());
    }

    @Test
    public void testActivate_startupNotCalled() {
        mCoordinator.onSurfaceOpened();

        // Calling to open the surface should not work because startup is not called.
        assertEquals(false, mCoordinator.isActive());
        assertEquals(false, hasStreamBound());
    }

    @Test
    public void testActivate_startupCalled() {
        FeedSurfaceTracker.getInstance().startup();

        // Startup should activate the coordinator and bind the feed.
        assertEquals(true, mCoordinator.isActive());
        assertEquals(true, hasStreamBound());
    }

    @Test
    public void testToggleSurfaceOpened() {
        FeedSurfaceTracker.getInstance().startup();
        mCoordinator.onSurfaceClosed();

        // Coordinator should be inactive because we closed the surface. Feed is unbound.
        assertEquals(false, mCoordinator.isActive());
        assertEquals(false, hasStreamBound());
    }

    @Test
    public void testActivate_feedHidden() {
        mCoordinator
                .getSectionHeaderModelForTest()
                .set(SectionHeaderListProperties.IS_SECTION_ENABLED_KEY, false);
        FeedSurfaceTracker.getInstance().startup();

        // After startup, coordinator should be active, but feed should not be bound.
        assertEquals(true, mCoordinator.isActive());
        assertEquals(false, hasStreamBound());
    }

    @Test
    public void testGetTabIdFromLaunchOrigin_webFeed() {
        assertEquals(
                FeedSurfaceCoordinator.StreamTabId.FOLLOWING,
                mCoordinator.getTabIdFromLaunchOrigin(NewTabPageLaunchOrigin.WEB_FEED));
    }

    @Test
    public void testGetTabIdFromLaunchOrigin_unknown() {
        assertEquals(
                FeedSurfaceCoordinator.StreamTabId.DEFAULT,
                mCoordinator.getTabIdFromLaunchOrigin(NewTabPageLaunchOrigin.UNKNOWN));
    }

    @Test
    public void testDisableReliabilityLogging_metricsReportingDisabled() {
        reset(mLaunchReliabilityLogger);
        mCoordinator.destroy();

        when(mPrivacyPreferencesManager.isMetricsReportingEnabled()).thenReturn(false);
        mCoordinator = createCoordinator();

        verify(mLaunchReliabilityLogger, never()).logUiStarting(anyInt(), anyLong());
    }

    @Test
    public void testLogUiStarting() {
        verify(mLaunchReliabilityLogger, times(1))
                .logUiStarting(SURFACE_TYPE, SURFACE_CREATION_TIME_NS);
    }

    @Test
    public void testActivityPaused() {
        when(mLaunchReliabilityLogger.isLaunchInProgress()).thenReturn(true);
        mCoordinator.onActivityPaused();
        verify(mLaunchReliabilityLogger, times(1))
                .logLaunchFinished(anyLong(), eq(DiscoverLaunchResult.FRAGMENT_PAUSED.getNumber()));
    }

    @Test
    public void testActivityResumed() {
        mCoordinator.onActivityResumed();
        verify(mLaunchReliabilityLogger, times(1)).cancelPendingFinished();
    }

    @Test
    public void testOmniboxFocused() {
        when(mLaunchReliabilityLogger.isLaunchInProgress()).thenReturn(true);
        mCoordinator.getReliabilityLogger().onOmniboxFocused();
        verify(mLaunchReliabilityLogger, times(1))
                .pendingFinished(anyLong(), eq(DiscoverLaunchResult.SEARCH_BOX_TAPPED.getNumber()));
    }

    @Test
    public void testVoiceSearch() {
        when(mLaunchReliabilityLogger.isLaunchInProgress()).thenReturn(true);
        mCoordinator.getReliabilityLogger().onVoiceSearch();
        verify(mLaunchReliabilityLogger, times(1))
                .pendingFinished(
                        anyLong(), eq(DiscoverLaunchResult.VOICE_SEARCH_TAPPED.getNumber()));
    }

    @Test
    public void testUrlFocusChange() {
        when(mLaunchReliabilityLogger.isLaunchInProgress()).thenReturn(true);
        mCoordinator.getReliabilityLogger().onUrlFocusChange(/* hasFocus= */ true);
        verify(mLaunchReliabilityLogger, never()).cancelPendingFinished();

        mCoordinator.getReliabilityLogger().onUrlFocusChange(/* hasFocus= */ false);
        verify(mLaunchReliabilityLogger, times(1)).cancelPendingFinished();
    }

    @Test
    public void testSetupHeaders_feedOn() {
        mCoordinator.setupHeaders(true);
        // Item count contains: feed header only
        assertEquals(1, mContentManagerCaptor.getValue().getItemCount());
    }

    @Test
    public void testSetupHeaders_feedOff() {
        mCoordinator.setupHeaders(false);
        // Item count contains: nothing, since ntp header is null
        assertEquals(0, mContentManagerCaptor.getValue().getItemCount());
    }

    @Test
    public void testNonSwipeRefresh() {
        mCoordinator.nonSwipeRefresh();
        verify(mMediatorSpy).manualRefresh(any());
        verify(mLaunchReliabilityLogger, times(1)).logManualRefresh(anyLong());
    }

    @Test
    public void testOnRefresh() {
        mCoordinator.onRefresh();
        verify(mMediatorSpy).manualRefresh(any());
        verify(mLaunchReliabilityLogger, times(1)).logManualRefresh(anyLong());
    }

    @Test
    public void testReload() {
        mCoordinator.reload();
        verify(mMediatorSpy).manualRefresh(any());
        verify(mLaunchReliabilityLogger, times(1)).logManualRefresh(anyLong());
    }

    @Test
    public void testSetUpLaunchReliabilityLogger() {
        reset(mLaunchReliabilityLogger);
        mCoordinator.destroy();
        when(mPrivacyPreferencesManager.isMetricsReportingEnabled()).thenReturn(true);
        mCoordinator = createCoordinator();

        verify(mLaunchReliabilityLogger, times(1))
                .logUiStarting(SURFACE_TYPE, SURFACE_CREATION_TIME_NS);
    }

    @Test
    public void testFeedHeaderPosition_scrollableContainerDelegate() {
        when(mScrollableContainerDelegate.getTopPositionRelativeToContainerView(any()))
                .thenReturn(-1);
        assertEquals(-1, mCoordinator.getFeedHeaderPosition());

        mCoordinator.clearScrollableContainerDelegateForTesting();
        assertEquals(Integer.MAX_VALUE, mCoordinator.getFeedHeaderPosition());
    }

    @Test
    public void testIsPrimaryAccountSupervisedForChildUser() {
        AccountInfo account = createFakeAccount(/* isChild= */ true);
        when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(account);
        when(mIdentityManager.findExtendedAccountInfoByEmailAddress(account.getEmail()))
                .thenReturn(account);
        when(mProfileMock.isChild()).thenReturn(true);

        assertTrue(mCoordinator.shouldDisplaySupervisedFeed());
    }

    @Test
    public void testIsPrimaryAccountSupervisedForRegularUser() {
        AccountInfo account = createFakeAccount(/* isChild= */ false);
        when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(account);
        when(mIdentityManager.findExtendedAccountInfoByEmailAddress(account.getEmail()))
                .thenReturn(account);
        assertFalse(mCoordinator.shouldDisplaySupervisedFeed());
    }

    @Test
    public void testIsPrimaryAccountSupervisedForSignedOutUser() {
        when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(null);
        assertFalse(mCoordinator.shouldDisplaySupervisedFeed());
    }

    @Test
    @DisableFeatures(ChromeFeatureList.TAB_STRIP_LAYOUT_OPTIMIZATION)
    public void testTabStripHeightChangeCallback() {
        ArgumentCaptor<Callback<Integer>> captor = ArgumentCaptor.forClass(Callback.class);
        verify(mTabStripHeightSupplier).addObserver(captor.capture());
        Callback<Integer> tabStripHeightChangeCallback = captor.getValue();
        tabStripHeightChangeCallback.onResult(mTabStripHeight);
        assertEquals(
                "Top padding of root view should be updated when tab strip height changes.",
                mTabStripHeight,
                mCoordinator.getRootViewForTesting().getPaddingTop());
    }

    private AccountInfo createFakeAccount(boolean isChild) {
        AccountCapabilities capabilities =
                new AccountCapabilities(
                        new HashMap<>(
                                Map.of(
                                        AccountCapabilitiesConstants
                                                .IS_SUBJECT_TO_PARENTAL_CONTROLS_CAPABILITY_NAME,
                                        isChild)));
        return new AccountInfo(
                new CoreAccountId("id"),
                "[email protected]",
                "gaiaId",
                "John Doe",
                "John",
                null,
                capabilities);
    }

    private boolean hasStreamBound() {
        if (mCoordinator.getMediatorForTesting().getCurrentStreamForTesting() == null) {
            return false;
        }
        return ((FeedStream) mCoordinator.getMediatorForTesting().getCurrentStreamForTesting())
                .getBoundStatusForTest();
    }

    private FeedSurfaceCoordinator createCoordinator() {
        return new FeedSurfaceCoordinator(
                mActivity,
                mSnackbarManager,
                mWindowAndroid,
                new PlaceholderJankTracker(),
                mSnapHelper,
                null,
                0,
                false,
                new TestSurfaceDelegate(),
                mProfileMock,
                mBottomSheetController,
                mShareDelegateSupplier,
                mScrollableContainerDelegate,
                NewTabPageLaunchOrigin.UNKNOWN,
                mPrivacyPreferencesManager,
                () -> {
                    return null;
                },
                SURFACE_CREATION_TIME_NS,
                null,
                false,
                /* viewportView= */ null,
                mFeedActionDelegate,
                mTabStripHeightSupplier);
    }
}