// Copyright 2015 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.device_dialog;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import androidx.core.util.ObjectsCompat;
import androidx.test.filters.SmallTest;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.permissions.DeviceItemAdapter;
import org.chromium.components.permissions.ItemChooserDialog;
import org.chromium.content_public.browser.test.util.TouchCommon;
import org.chromium.ui.widget.TextViewWithClickableSpans;
/**
* Tests for the ItemChooserDialog class.
*
* <p>TODO(crbug.com/40187298): Componentize this test.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
// TODO(crbug.com/344665244): Failing when batched, batch this again.
public class ItemChooserDialogTest implements ItemChooserDialog.ItemSelectedCallback {
@ClassRule
public static final ChromeTabbedActivityTestRule sActivityTestRule =
new ChromeTabbedActivityTestRule();
@Rule
public final BlankCTATabInitialStateRule mInitialStateRule =
new BlankCTATabInitialStateRule(sActivityTestRule, false);
ItemChooserDialog mChooserDialog;
String mLastSelectedId = "None";
Drawable mTestDrawable1;
String mTestDrawableDescription1;
Drawable mTestDrawable2;
String mTestDrawableDescription2;
@Before
public void setUp() throws Exception {
mChooserDialog = createDialog();
mTestDrawable1 = getNewTestDrawable();
mTestDrawableDescription1 = "icon1 description";
mTestDrawable2 = getNewTestDrawable();
mTestDrawableDescription2 = "icon2 description";
Assert.assertFalse(ObjectsCompat.equals(mTestDrawable1, mTestDrawable2));
}
@After
public void tearDown() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
mChooserDialog.setIdleState();
mChooserDialog.dismiss();
});
}
// ItemChooserDialog.ItemSelectedCallback:
@Override
public void onItemSelected(String id) {
mLastSelectedId = id;
}
private Drawable getNewTestDrawable() {
final Activity activity = sActivityTestRule.getActivity();
Drawable drawable =
VectorDrawableCompat.create(
activity.getResources(),
R.drawable.ic_bluetooth_connected,
activity.getTheme());
// Calling mutate() on a Drawable should typically create a new ConstantState
// for that Drawable. Ensure the new drawable doesn't share a state with other
// drwables.
return drawable.mutate();
}
private ItemChooserDialog createDialog() {
SpannableString title = new SpannableString("title");
SpannableString searching = new SpannableString("searching");
SpannableString noneFound = new SpannableString("noneFound");
SpannableString statusActive = new SpannableString("statusActive");
SpannableString statusIdleNoneFound = new SpannableString("statusIdleNoneFound");
SpannableString statusIdleSomeFound = new SpannableString("statusIdleSomeFound");
String positiveButton = new String("positiveButton");
ItemChooserDialog.ItemChooserLabels labels =
new ItemChooserDialog.ItemChooserLabels(
title,
searching,
noneFound,
statusActive,
statusIdleNoneFound,
statusIdleSomeFound,
positiveButton);
Activity activity = sActivityTestRule.getActivity();
return ThreadUtils.runOnUiThreadBlocking(
() -> {
return new ItemChooserDialog(
activity, activity.getWindow(), ItemChooserDialogTest.this, labels);
});
}
private void selectItem(
Dialog dialog, int position, String expectedItemId, boolean expectedEnabledState) {
final ListView items = dialog.findViewById(R.id.items);
final Button button = dialog.findViewById(R.id.positive);
CriteriaHelper.pollUiThread(
() -> Criteria.checkThat(items.getChildAt(0), Matchers.notNullValue()));
// Verify first item selected gets selected.
TouchCommon.singleClickView(items.getChildAt(position - 1));
CriteriaHelper.pollUiThread(
() -> Criteria.checkThat(button.isEnabled(), Matchers.is(expectedEnabledState)));
if (!expectedEnabledState) return;
TouchCommon.singleClickView(button);
CriteriaHelper.pollUiThread(
() -> Criteria.checkThat(mLastSelectedId, Matchers.is(expectedItemId)));
}
private View getRowView(Dialog dialog, int position) {
ListView items = dialog.findViewById(R.id.items);
int actualPosition = position - 1;
int first = items.getFirstVisiblePosition();
int last = items.getLastVisiblePosition();
if (actualPosition < first || actualPosition > last) {
return items.getAdapter().getView(actualPosition, null, items);
} else {
final int visiblePos = actualPosition - first;
return items.getChildAt(visiblePos);
}
}
private ImageView getIconImageView(Dialog dialog, int position) {
return (ImageView) getRowView(dialog, position).findViewById(R.id.icon);
}
private TextView getDescriptionTextView(Dialog dialog, int position) {
return (TextView) getRowView(dialog, position).findViewById(R.id.description);
}
@Test
@SmallTest
public void testAddItemsWithNoIcons() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with no icon.
mChooserDialog.addOrUpdateItem("key1", "desc1");
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon1.getVisibility());
Assert.assertEquals(null, icon1.getDrawable());
}
{
// Add item 2 with no icon.
mChooserDialog.addOrUpdateItem("key2", "desc2");
ImageView icon2 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon2.getVisibility());
Assert.assertEquals(null, icon2.getDrawable());
}
});
}
@Test
@SmallTest
public void testAddItemsWithIcons() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
Assert.assertEquals(
mTestDrawableDescription1, icon1.getContentDescription());
}
{
// Add item 2 with icon.
mChooserDialog.addOrUpdateItem(
"key2", "desc2", mTestDrawable2, mTestDrawableDescription2);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
Assert.assertEquals(
mTestDrawableDescription1, icon1.getContentDescription());
ImageView icon2 = getIconImageView(dialog, 2);
Assert.assertEquals(View.VISIBLE, icon2.getVisibility());
Assert.assertEquals(mTestDrawable2, icon2.getDrawable());
Assert.assertEquals(
mTestDrawableDescription2, icon2.getContentDescription());
}
});
}
@Test
@SmallTest
public void testAddItemWithIconAfterItemWithNoIcon() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with no icon.
mChooserDialog.addOrUpdateItem("key1", "desc1");
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon1.getVisibility());
Assert.assertEquals(null, icon1.getDrawable());
}
{
// Add item 2 with icon.
mChooserDialog.addOrUpdateItem(
"key2", "desc2", mTestDrawable2, mTestDrawableDescription2);
ImageView icon1 = getIconImageView(dialog, 1);
ImageView icon2 = getIconImageView(dialog, 2);
Assert.assertEquals(View.INVISIBLE, icon1.getVisibility());
Assert.assertEquals(View.VISIBLE, icon2.getVisibility());
Assert.assertEquals(mTestDrawable2, icon2.getDrawable());
}
});
}
@Test
@SmallTest
public void testAddItemWithNoIconAfterItemWithIcon() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
}
{
// Add item 2 with no icon.
mChooserDialog.addOrUpdateItem("key2", "desc2");
ImageView icon1 = getIconImageView(dialog, 1);
ImageView icon2 = getIconImageView(dialog, 2);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
Assert.assertEquals(View.INVISIBLE, icon2.getVisibility());
}
});
}
@Test
@SmallTest
public void testRemoveItemWithIconNoItemsWithIconsLeft() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
}
{
// Add item 2 with no icon.
mChooserDialog.addOrUpdateItem("key2", "desc2");
ImageView icon1 = getIconImageView(dialog, 1);
ImageView icon2 = getIconImageView(dialog, 2);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(View.INVISIBLE, icon2.getVisibility());
}
{
// Remove item 1 with icon. No items with icons left.
mChooserDialog.removeItemFromList("key1");
ImageView icon2 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon2.getVisibility());
}
});
}
@Test
@SmallTest
public void testRemoveItemWithIconOneItemWithIconLeft() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
{
// Add item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
}
{
// Add item 2 with icon.
mChooserDialog.addOrUpdateItem(
"key2", "desc2", mTestDrawable2, mTestDrawableDescription2);
ImageView icon1 = getIconImageView(dialog, 1);
ImageView icon2 = getIconImageView(dialog, 2);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(View.VISIBLE, icon2.getVisibility());
}
{
// Add item 3 with no icon.
mChooserDialog.addOrUpdateItem("key3", "desc3");
ImageView icon1 = getIconImageView(dialog, 1);
ImageView icon2 = getIconImageView(dialog, 2);
ImageView icon3 = getIconImageView(dialog, 3);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(View.VISIBLE, icon2.getVisibility());
Assert.assertEquals(View.INVISIBLE, icon3.getVisibility());
}
{
mChooserDialog.removeItemFromList("key1");
ImageView icon2 = getIconImageView(dialog, 1);
ImageView icon3 = getIconImageView(dialog, 2);
Assert.assertEquals(View.VISIBLE, icon2.getVisibility());
Assert.assertEquals(mTestDrawable2, icon2.getDrawable());
Assert.assertEquals(View.INVISIBLE, icon3.getVisibility());
}
});
}
@Test
@SmallTest
public void testUpdateItemWithIconToNoIcon() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
{
// Add item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(
mTestDrawableDescription1, icon1.getContentDescription());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
mTestDrawable1,
mTestDrawableDescription1));
}
{
// Update item 1 to no icon.
mChooserDialog.addOrUpdateItem("key1", "desc1");
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon1.getVisibility());
Assert.assertEquals(null, icon1.getContentDescription());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
}
});
}
@Test
@SmallTest
public void testUpdateItemWithNoIconToIcon() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
{
// Add item 1 to no icon.
mChooserDialog.addOrUpdateItem("key1", "desc1");
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.GONE, icon1.getVisibility());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
}
{
// Update item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
Assert.assertEquals(
mTestDrawableDescription1, icon1.getContentDescription());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
mTestDrawable1,
mTestDrawableDescription1));
}
});
}
@Test
@SmallTest
public void testUpdateItemIcon() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
{
// Update item 1 with icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable1, mTestDrawableDescription1);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable1, icon1.getDrawable());
Assert.assertEquals(
mTestDrawableDescription1, icon1.getContentDescription());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
mTestDrawable1,
mTestDrawableDescription1));
}
{
// Update item 1 with different icon.
mChooserDialog.addOrUpdateItem(
"key1", "desc1", mTestDrawable2, mTestDrawableDescription2);
ImageView icon1 = getIconImageView(dialog, 1);
Assert.assertEquals(View.VISIBLE, icon1.getVisibility());
Assert.assertEquals(mTestDrawable2, icon1.getDrawable());
Assert.assertEquals(
mTestDrawableDescription2, icon1.getContentDescription());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
mTestDrawable2,
mTestDrawableDescription2));
}
});
}
@Test
@SmallTest
public void testSimpleItemSelection() {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
TextViewWithClickableSpans statusView = dialog.findViewById(R.id.status);
final ListView items = dialog.findViewById(R.id.items);
final Button button = dialog.findViewById(R.id.positive);
// Before we add items to the dialog, the 'searching' message should be
// showing, the Commit button should be disabled and the list view hidden.
Assert.assertEquals("searching", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
Assert.assertEquals(View.GONE, items.getVisibility());
ThreadUtils.runOnUiThreadBlocking(
() -> {
mChooserDialog.addOrUpdateItem("key1", "desc1");
mChooserDialog.addOrUpdateItem("key2", "desc2");
});
// Two items showing, the empty view should be no more and the button
// remains disabled.
Assert.assertEquals(View.VISIBLE, items.getVisibility());
Assert.assertEquals(View.GONE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusActive", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
ThreadUtils.runOnUiThreadBlocking(
() -> {
mChooserDialog.setIdleState();
});
// After discovery stops the list should be visible with two items,
// it should not show the empty view and the button should not be enabled.
// The chooser should show the status idle text.
Assert.assertEquals(View.VISIBLE, items.getVisibility());
Assert.assertEquals(View.GONE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusIdleSomeFound", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
// Select the first item and verify it got selected. The "Pair" button
// should now be enabled.
selectItem(dialog, 1, "key1", true);
Assert.assertTrue(getDescriptionTextView(dialog, 1).isSelected());
Assert.assertTrue(button.isEnabled());
}
@Test
@SmallTest
public void testNoItemsAddedDiscoveryIdle() {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
TextViewWithClickableSpans statusView = dialog.findViewById(R.id.status);
final ListView items = dialog.findViewById(R.id.items);
final Button button = dialog.findViewById(R.id.positive);
// Before we add items to the dialog, the 'searching' message should be
// showing, the Commit button should be disabled and the list view hidden.
Assert.assertEquals("searching", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
Assert.assertEquals(View.GONE, items.getVisibility());
ThreadUtils.runOnUiThreadBlocking(
() -> {
mChooserDialog.setIdleState();
});
// Listview should now be showing empty, with an empty view visible to
// drive home the point and a status message at the bottom.
Assert.assertEquals(View.GONE, items.getVisibility());
Assert.assertEquals(View.VISIBLE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusIdleNoneFound", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
}
@Test
@SmallTest
public void testPairButtonDisabledAfterSelectedItemRemoved() throws Throwable {
final Dialog dialog =
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog1 = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog1.isShowing());
mChooserDialog.addOrUpdateItem("key1", "desc1");
mChooserDialog.addOrUpdateItem("key2", "desc2");
return dialog1;
});
selectItem(dialog, 1, "key1", true);
ThreadUtils.runOnUiThreadBlocking(
() -> {
final Button button = dialog.findViewById(R.id.positive);
Assert.assertTrue(button.isEnabled());
mChooserDialog.removeItemFromList("key1");
Assert.assertFalse(button.isEnabled());
});
}
@Test
@SmallTest
public void testSelectAnItemAndRemoveAnotherItem() throws Throwable {
final Dialog dialog =
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog1 = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog1.isShowing());
mChooserDialog.addOrUpdateItem("key1", "desc1");
mChooserDialog.addOrUpdateItem("key2", "desc2");
mChooserDialog.addOrUpdateItem("key3", "desc3");
return dialog1;
});
selectItem(dialog, 2, "key2", true);
ThreadUtils.runOnUiThreadBlocking(
() -> {
final Button button = dialog.findViewById(R.id.positive);
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
Assert.assertTrue(button.isEnabled());
// Remove the item before the currently selected item.
mChooserDialog.removeItemFromList("key1");
Assert.assertTrue(button.isEnabled());
Assert.assertEquals("key2", itemAdapter.getSelectedItemKey());
// Remove the item after the currently selected item.
mChooserDialog.removeItemFromList("key3");
Assert.assertTrue(button.isEnabled());
Assert.assertEquals("key2", itemAdapter.getSelectedItemKey());
});
}
@Test
@SmallTest
public void testSelectAnItemAndRemoveTheSelectedItem() throws Throwable {
final Dialog dialog =
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog1 = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog1.isShowing());
mChooserDialog.addOrUpdateItem("key1", "desc1");
mChooserDialog.addOrUpdateItem("key2", "desc2");
mChooserDialog.addOrUpdateItem("key3", "desc3");
return dialog1;
});
selectItem(dialog, 2, "key2", true);
ThreadUtils.runOnUiThreadBlocking(
() -> {
Button button = dialog.findViewById(R.id.positive);
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
Assert.assertTrue(button.isEnabled());
// Remove the selected item.
mChooserDialog.removeItemFromList("key2");
Assert.assertFalse(button.isEnabled());
Assert.assertEquals("", itemAdapter.getSelectedItemKey());
});
}
@Test
@SmallTest
public void testUpdateItemAndRemoveItemFromList() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
TextViewWithClickableSpans statusView = dialog.findViewById(R.id.status);
final ListView items = dialog.findViewById(R.id.items);
final Button button = dialog.findViewById(R.id.positive);
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
final String nonExistentKey = "key";
// Initially the itemAdapter is empty.
Assert.assertTrue(itemAdapter.isEmpty());
// Try removing an item from an empty itemAdapter.
mChooserDialog.removeItemFromList(nonExistentKey);
Assert.assertTrue(itemAdapter.isEmpty());
// Add item 1.
mChooserDialog.addOrUpdateItem("key1", "desc1");
Assert.assertEquals(1, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
// Update item 1 with different description.
mChooserDialog.addOrUpdateItem("key1", "desc2");
Assert.assertEquals(1, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc2",
/* icon= */ null,
/* iconDescription= */ null));
mChooserDialog.setIdleState();
// Remove item 1.
mChooserDialog.removeItemFromList("key1");
Assert.assertTrue(itemAdapter.isEmpty());
// Listview should now be showing empty, with an empty view visible
// and the button should not be enabled.
// The chooser should show a status message at the bottom.
Assert.assertEquals(View.GONE, items.getVisibility());
Assert.assertEquals(View.VISIBLE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusIdleNoneFound", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
});
}
@Test
@SmallTest
public void testAddItemAndRemoveItemFromList() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
TextViewWithClickableSpans statusView = dialog.findViewById(R.id.status);
final ListView items = dialog.findViewById(R.id.items);
final Button button = dialog.findViewById(R.id.positive);
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
final String nonExistentKey = "key";
// Initially the itemAdapter is empty.
Assert.assertTrue(itemAdapter.isEmpty());
// Try removing an item from an empty itemAdapter.
mChooserDialog.removeItemFromList(nonExistentKey);
Assert.assertTrue(itemAdapter.isEmpty());
// Add item 1.
mChooserDialog.addOrUpdateItem("key1", "desc1");
Assert.assertEquals(1, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
// Add item 2.
mChooserDialog.addOrUpdateItem("key2", "desc2");
Assert.assertEquals(2, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
Assert.assertTrue(
itemAdapter
.getItem(1)
.hasSameContents(
"key2",
"desc2",
/* icon= */ null,
/* iconDescription= */ null));
mChooserDialog.setIdleState();
// Try removing an item that doesn't exist.
mChooserDialog.removeItemFromList(nonExistentKey);
Assert.assertEquals(2, itemAdapter.getCount());
// Remove item 2.
mChooserDialog.removeItemFromList("key2");
Assert.assertEquals(1, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
// The list should be visible with one item, it should not show
// the empty view and the button should not be enabled.
// The chooser should show a status message at the bottom.
Assert.assertEquals(View.VISIBLE, items.getVisibility());
Assert.assertEquals(View.GONE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusIdleSomeFound", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
// Remove item 1.
mChooserDialog.removeItemFromList("key1");
Assert.assertTrue(itemAdapter.isEmpty());
// Listview should now be showing empty, with an empty view visible
// and the button should not be enabled.
// The chooser should show a status message at the bottom.
Assert.assertEquals(View.GONE, items.getVisibility());
Assert.assertEquals(View.VISIBLE, items.getEmptyView().getVisibility());
Assert.assertEquals("statusIdleNoneFound", statusView.getText().toString());
Assert.assertFalse(button.isEnabled());
});
}
@Test
@SmallTest
public void testAddItemWithSameNameToListAndRemoveItemFromList() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Dialog dialog = mChooserDialog.getDialogForTesting();
Assert.assertTrue(dialog.isShowing());
DeviceItemAdapter itemAdapter = mChooserDialog.getItemAdapterForTesting();
// Add item 1.
mChooserDialog.addOrUpdateItem("key1", "desc1");
Assert.assertEquals(1, itemAdapter.getCount());
// Add item 2.
mChooserDialog.addOrUpdateItem("key2", "desc2");
Assert.assertEquals(2, itemAdapter.getCount());
// Add item 3 with same description as item 1.
mChooserDialog.addOrUpdateItem("key3", "desc1");
Assert.assertEquals(3, itemAdapter.getCount());
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
Assert.assertTrue(
itemAdapter
.getItem(1)
.hasSameContents(
"key2",
"desc2",
/* icon= */ null,
/* iconDescription= */ null));
Assert.assertTrue(
itemAdapter
.getItem(2)
.hasSameContents(
"key3",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
// Since two items have the same name, their display text should have their
// unique keys appended.
Assert.assertEquals("desc1 (key1)", itemAdapter.getDisplayText(0));
Assert.assertEquals("desc2", itemAdapter.getDisplayText(1));
Assert.assertEquals("desc1 (key3)", itemAdapter.getDisplayText(2));
// Remove item 2.
mChooserDialog.removeItemFromList("key2");
Assert.assertEquals(2, itemAdapter.getCount());
// Make sure the remaining items are item 1 and item 3.
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key1",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
Assert.assertTrue(
itemAdapter
.getItem(1)
.hasSameContents(
"key3",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
Assert.assertEquals("desc1 (key1)", itemAdapter.getDisplayText(0));
Assert.assertEquals("desc1 (key3)", itemAdapter.getDisplayText(1));
// Remove item 1.
mChooserDialog.removeItemFromList("key1");
Assert.assertEquals(1, itemAdapter.getCount());
// Make sure the remaining item is item 3.
Assert.assertTrue(
itemAdapter
.getItem(0)
.hasSameContents(
"key3",
"desc1",
/* icon= */ null,
/* iconDescription= */ null));
// After removing item 1, item 3 is the only remaining item, so its display text
// also changed to its original description.
Assert.assertEquals("desc1", itemAdapter.getDisplayText(0));
});
}
@Test
@SmallTest
public void testListHeight() {
// 500 * .3 is 150, which is 48 * 3.125. 48 * 3.5 is 168.
Assert.assertEquals(168, ItemChooserDialog.getListHeight(500, 1.0f));
// 150 * .3 is 45, which rounds below the minimum height.
Assert.assertEquals(72, ItemChooserDialog.getListHeight(150, 1.0f));
// 1460 * .3 is 438, which rounds above the maximum height.
Assert.assertEquals(408, ItemChooserDialog.getListHeight(1460, 1.0f));
// 1100px is 500dp at a density of 2.2. 500 * .3 is 150dp, which is 48dp *
// 3.125. 48dp * 3.5 is 168dp. 168dp * 2.2px/dp is 369.6, which rounds to
// 370.
Assert.assertEquals(370, ItemChooserDialog.getListHeight(1100, 2.2f));
}
}