chromium/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java

// Copyright 2019 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.tasks.tab_management;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.areAnimatorsEnabled;

import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tasks.tab_management.TabGridDialogView.VisibilityListener;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.ui.test.util.BlankUiTestActivityTestCase;

import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

/** BlankUiTestActivity Tests for the {@link TabGridDialogView}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@DisableFeatures({ChromeFeatureList.DATA_SHARING})
@Batch(Batch.UNIT_TESTS)
public class TabGridDialogViewTest extends BlankUiTestActivityTestCase {
    private int mMinMargin;
    private int mMaxMargin;
    private FrameLayout mTestParent;
    private View mSourceView;
    private View mUngroupBar;
    private View mAnimationCardView;
    private View mBackgroundFrameView;
    private TextView mUngroupBarTextView;
    private RelativeLayout mTabGridDialogContainer;
    private FrameLayout.LayoutParams mContainerParams;
    private TabGridDialogView mTabGridDialogView;

    @Override
    public void setUpTest() throws Exception {
        super.setUpTest();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTestParent = new FrameLayout(getActivity());
                    getActivity().setContentView(mTestParent);
                    LayoutInflater.from(getActivity())
                            .inflate(R.layout.tab_grid_dialog_layout, mTestParent, true);

                    mTabGridDialogView = mTestParent.findViewById(R.id.dialog_parent_view);
                    mTabGridDialogContainer =
                            mTabGridDialogView.findViewById(R.id.dialog_container_view);
                    mUngroupBar = mTabGridDialogContainer.findViewById(R.id.dialog_ungroup_bar);
                    mUngroupBarTextView = mUngroupBar.findViewById(R.id.dialog_ungroup_bar_text);
                    mContainerParams =
                            (FrameLayout.LayoutParams) mTabGridDialogContainer.getLayoutParams();
                    mAnimationCardView =
                            mTabGridDialogView.findViewById(R.id.dialog_animation_card_view);
                    mBackgroundFrameView = mTabGridDialogView.findViewById(R.id.dialog_frame);
                    ScrimCoordinator scrimCoordinator =
                            new ScrimCoordinator(getActivity(), null, mTestParent, Color.RED);
                    mTabGridDialogView.setupScrimCoordinator(scrimCoordinator);
                    mTabGridDialogView.setScrimClickRunnable(() -> {});

                    mMinMargin =
                            getActivity()
                                    .getResources()
                                    .getDimensionPixelSize(R.dimen.tab_grid_dialog_min_margin);
                    mMaxMargin =
                            getActivity()
                                    .getResources()
                                    .getDimensionPixelSize(R.dimen.tab_grid_dialog_max_margin);
                });
    }

    @Test
    @SmallTest
    @UiThreadTest
    public void testUpdateDialogWithOrientation() {
        mockDialogStatus(false);

        mTabGridDialogView.updateDialogWithOrientation(Configuration.ORIENTATION_PORTRAIT);

        assertThat(
                mContainerParams.topMargin,
                allOf(greaterThanOrEqualTo(mMinMargin), lessThanOrEqualTo(mMaxMargin)));
        assertEquals(mContainerParams.leftMargin, mMinMargin);
        assertEquals(View.GONE, mTabGridDialogView.getVisibility());

        mockDialogStatus(false);

        mTabGridDialogView.updateDialogWithOrientation(Configuration.ORIENTATION_LANDSCAPE);

        assertThat(
                mContainerParams.leftMargin,
                allOf(greaterThanOrEqualTo(mMinMargin), lessThanOrEqualTo(mMaxMargin)));
        assertEquals(mContainerParams.topMargin, mMinMargin);
        assertEquals(View.GONE, mTabGridDialogView.getVisibility());

        mockDialogStatus(true);

        mTabGridDialogView.updateDialogWithOrientation(Configuration.ORIENTATION_PORTRAIT);

        assertThat(
                mContainerParams.topMargin,
                allOf(greaterThanOrEqualTo(mMinMargin), lessThanOrEqualTo(mMaxMargin)));
        assertEquals(mContainerParams.leftMargin, mMinMargin);
        assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());

        mockDialogStatus(true);

        mTabGridDialogView.updateDialogWithOrientation(Configuration.ORIENTATION_LANDSCAPE);

        assertThat(
                mContainerParams.leftMargin,
                allOf(greaterThanOrEqualTo(mMinMargin), lessThanOrEqualTo(mMaxMargin)));
        assertEquals(mContainerParams.topMargin, mMinMargin);
        assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
    }

    @Test
    @SmallTest
    @UiThreadTest
    public void testResetDialog() {
        View toolbarView = new View(getActivity());
        View recyclerView = new View(getActivity());
        recyclerView.setVisibility(View.GONE);

        mTabGridDialogView.resetDialog(toolbarView, recyclerView);

        assertEquals(
                getActivity().findViewById(R.id.tab_grid_dialog_toolbar_container),
                toolbarView.getParent());
        assertEquals(
                getActivity().findViewById(R.id.tab_grid_dialog_recycler_view_container),
                recyclerView.getParent());
        assertEquals(View.VISIBLE, recyclerView.getVisibility());
    }

    @Test
    @MediumTest
    public void testUpdateUngroupBar() {
        AtomicReference<ColorStateList> showTextColorReference = new AtomicReference<>();
        AtomicReference<ColorStateList> hoverTextColorReference = new AtomicReference<>();
        // Initialize the dialog with stand-in views.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    View toolbarView = new View(getActivity());
                    View recyclerView = new View(getActivity());
                    mTabGridDialogView.resetDialog(toolbarView, recyclerView);
                });

        // From hide to show.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ColorStateList colorStateList = mUngroupBarTextView.getTextColors();
                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.SHOW);

                    assertNotNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is updated.
                    assertNotEquals(colorStateList, mUngroupBarTextView.getTextColors());
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                    // Initialize text color when the ungroup bar is showing.
                    showTextColorReference.set(mUngroupBarTextView.getTextColors());
                });

        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting(),
                                Matchers.nullValue()));

        // From show to hide.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.HIDE);

                    assertNotNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is not updated.
                    assertEquals(showTextColorReference.get(), mUngroupBarTextView.getTextColors());
                    // Ungroup bar is still visible for the hiding animation.
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                });

        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting(),
                                Matchers.nullValue()));
        // Ungroup bar is not visible after the hiding animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> assertEquals(View.INVISIBLE, mUngroupBar.getVisibility()));

        // From hide to hover.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    ColorStateList colorStateList = mUngroupBarTextView.getTextColors();
                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.HOVERED);

                    assertNotNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is updated.
                    assertNotEquals(colorStateList, mUngroupBarTextView.getTextColors());
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                    // Initialize text color when the ungroup bar is being hovered on.
                    hoverTextColorReference.set(mUngroupBarTextView.getTextColors());
                });

        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting(),
                                Matchers.nullValue()));

        // From hover to hide.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.HIDE);

                    assertNotNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is not updated.
                    assertEquals(
                            hoverTextColorReference.get(), mUngroupBarTextView.getTextColors());
                    // Ungroup bar is still visible for the hiding animation.
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                });

        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting(),
                                Matchers.nullValue()));
        // Ungroup bar is not visible after the hiding animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> assertEquals(View.INVISIBLE, mUngroupBar.getVisibility()));

        // From show to hover.
        // First, set the ungroup bar state to show.
        ThreadUtils.runOnUiThreadBlocking(
                () -> mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.SHOW));
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting(),
                                Matchers.nullValue()));

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(showTextColorReference.get(), mUngroupBarTextView.getTextColors());

                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.HOVERED);

                    // There should be no animation going on.
                    assertNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is updated.
                    assertEquals(
                            hoverTextColorReference.get(), mUngroupBarTextView.getTextColors());
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                });

        // From hover to show.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            hoverTextColorReference.get(), mUngroupBarTextView.getTextColors());

                    mTabGridDialogView.updateUngroupBar(TabGridDialogView.UngroupBarStatus.SHOW);

                    // There should be no animation going on.
                    assertNull(mTabGridDialogView.getCurrentUngroupBarAnimatorForTesting());
                    // Verify that the ungroup bar textView is updated.
                    assertEquals(showTextColorReference.get(), mUngroupBarTextView.getTextColors());
                    assertEquals(View.VISIBLE, mUngroupBar.getVisibility());
                });
    }

    @Test
    @MediumTest
    public void testDialog_ZoomInZoomOut() {
        // TODO(crbug.com/40687819): figure out a stable way to separate different stages of the
        // animation so that we can verify the alpha and view hierarchy of the animation-related
        // views.
        AtomicReference<ViewGroup> parentViewReference = new AtomicReference<>();
        // Setup the animation with a stand-in animation source view.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mSourceView = new View(getActivity());
                    mTestParent.addView(mSourceView, 0, new FrameLayout.LayoutParams(100, 100));
                });
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.setupDialogAnimation(mSourceView);
                    parentViewReference.set((ViewGroup) mTabGridDialogContainer.getParent());
                    assertFalse(mTabGridDialogContainer.isFocused());
                });
        ViewGroup parent = parentViewReference.get();

        // Show the dialog with zoom-out animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.showDialog();
                    assertNotNull(mTabGridDialogView.getCurrentDialogAnimatorForTesting());
                    assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
                });
        // When the card fades out, the dialog should be brought to the top, and alpha of animation
        // related views should be set to 0.
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            parent.getChildAt(parent.getChildCount() - 1),
                            Matchers.is(mTabGridDialogContainer));
                });
        ThreadUtils.runOnUiThreadBlocking(
                () -> assertEquals(0f, mAnimationCardView.getAlpha(), 0.0));
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertTrue(mTabGridDialogContainer.isFocused());
                });

        // Hide the dialog with zoom-in animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.hideDialog();
                    assertNotNull(mTabGridDialogView.getCurrentDialogAnimatorForTesting());
                    // PopupWindow is still showing for the hide animation.
                    assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
                });
        // When the dialog fades out, the animation card and the background frame should be brought
        // to the top.
        CriteriaHelper.pollUiThread(
                () ->
                        mAnimationCardView
                                        == ((ViewGroup)
                                                        parent.getChildAt(
                                                                parent.getChildCount() - 1))
                                                .getChildAt(0)
                                && mBackgroundFrameView
                                        == parent.getChildAt(parent.getChildCount() - 2));
        ThreadUtils.runOnUiThreadBlocking(
                () -> assertEquals(0f, mTabGridDialogContainer.getAlpha(), 0.0));
        // When the animation completes, the PopupWindow should be dismissed.
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(View.GONE, mTabGridDialogView.getVisibility());
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertEquals(0f, mTabGridDialogContainer.getTranslationX(), 0.0);
                    assertEquals(0f, mTabGridDialogContainer.getTranslationY(), 0.0);
                    assertEquals(1f, mTabGridDialogContainer.getScaleX(), 0.0);
                    assertEquals(1f, mTabGridDialogContainer.getScaleY(), 0.0);
                    assertFalse(mTabGridDialogContainer.isFocused());
                });
    }

    @Test
    @MediumTest
    public void testDialog_ZoomInFadeOut() {
        // Setup the animation with a stand-in animation source view.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mSourceView = new View(getActivity());
                    mTestParent.addView(mSourceView, 0, new FrameLayout.LayoutParams(100, 100));
                });
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.setupDialogAnimation(mSourceView);
                    assertFalse(mTabGridDialogContainer.isFocused());
                });
        // Show the dialog.
        ThreadUtils.runOnUiThreadBlocking(() -> mTabGridDialogView.showDialog());
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));
        // After the zoom in animation, alpha of animation related views should be 0.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertTrue(mTabGridDialogContainer.isFocused());
                });

        // Hide the dialog with basic fade-out animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.setupDialogAnimation(null);
                    mTabGridDialogView.hideDialog();
                    // Alpha of animation related views should remain 0.
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertNotNull(mTabGridDialogView.getCurrentDialogAnimatorForTesting());
                    assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
                });
        // When the animation completes, the PopupWindow should be dismissed. The alpha of animation
        // related views should remain 0.
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(View.GONE, mTabGridDialogView.getVisibility());
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertFalse(mTabGridDialogContainer.isFocused());
                });
    }

    @Test
    @MediumTest
    public void testDialog_FadeInFadeOut() {
        // Setup the the basic fade-in and fade-out animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.setupDialogAnimation(null);
                    // Initially alpha of animation related views should be 0.
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertFalse(mTabGridDialogContainer.isFocused());
                });

        // Show the dialog with basic fade-in animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.showDialog();
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertNotNull(mTabGridDialogView.getCurrentDialogAnimatorForTesting());
                    assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
                });
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertTrue(mTabGridDialogContainer.isFocused());
                });

        // Hide the dialog with basic fade-out animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.hideDialog();
                    if (areAnimatorsEnabled()) {
                        // At the very beginning of hiding animation, alpha of background frame and
                        // animation card should both be set to 0f.
                        assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                        assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    }
                    assertNotNull(mTabGridDialogView.getCurrentDialogAnimatorForTesting());
                    assertEquals(View.VISIBLE, mTabGridDialogView.getVisibility());
                });
        // When the animation completes, the PopupWindow should be dismissed. The alpha of animation
        // related views should remain 0.
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(View.GONE, mTabGridDialogView.getVisibility());
                    assertEquals(0f, mAnimationCardView.getAlpha(), 0.0);
                    assertEquals(0f, mBackgroundFrameView.getAlpha(), 0.0);
                    assertFalse(mTabGridDialogContainer.isFocused());
                });
    }

    @Test
    @MediumTest
    public void testHideDialog_InvokeVisibilityListener() throws TimeoutException {
        CallbackHelper visibilityCallback = new CallbackHelper();
        mTabGridDialogView.setVisibilityListener(
                new VisibilityListener() {
                    @Override
                    public void finishedHidingDialogView() {
                        visibilityCallback.notifyCalled();
                    }
                });
        // Setup the the basic animation.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.setupDialogAnimation(null);
                });

        // Show the dialog.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.showDialog();
                });
        // Wait for show to finish.
        CriteriaHelper.pollUiThread(
                () ->
                        Criteria.checkThat(
                                mTabGridDialogView.getCurrentDialogAnimatorForTesting(),
                                Matchers.nullValue()));

        // Hide the dialog.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.hideDialog();
                });
        visibilityCallback.waitForNext();
    }

    @Test
    @SmallTest
    public void testDispatchTouchEvent() {
        boolean[] isFocused = new boolean[] {false};
        boolean[] isFocusCleared = new boolean[] {false};
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    EditText textView =
                            new EditText(getActivity()) {
                                @Override
                                public boolean isFocused() {
                                    return isFocused[0];
                                }

                                @Override
                                public void clearFocus() {
                                    isFocusCleared[0] = true;
                                }
                            };
                    textView.setId(R.id.title);
                    mTabGridDialogView.addView(textView);
                });

        long time = SystemClock.uptimeMillis();
        MotionEvent event = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0.f, 0.f, 0);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.dispatchTouchEvent(event);
                });
        assertFalse(isFocusCleared[0]);

        isFocused[0] = true;
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTabGridDialogView.dispatchTouchEvent(event);
                });
        assertTrue(isFocusCleared[0]);
    }

    private void mockDialogStatus(boolean isShowing) {
        mContainerParams.setMargins(0, 0, 0, 0);
        if (isShowing) {
            mTabGridDialogView.setVisibility(View.VISIBLE);
        } else {
            mTabGridDialogView.setVisibility(View.GONE);
        }
    }
}