Commit 570c75d6 authored by Yue Zhang's avatar Yue Zhang Committed by Commit Bot

[a11y] Update content description for dialog back button

This CL updates the content description of the dialog back button so
that it is announced as "Collapse {title} tab group with {tabCount}
tabs" when focused in a11y mode.

Everything introduced in this CL is gated by Finch parameter
"enable_launch_polish" under flag "enable-tab-grid-layout", with gate
function TabUiFeatureUtilities#isLaunchPolishEnabled. This is
verified by formal equivalence tool in http://crrev.com/c/1934235.
The only diff exposed by the tool is in TabGridPanelProperties.java
which is essentially no-op when the flag is off.

Bug: 1127389
Change-Id: Id1cc480889cfd6a71a2a3fcabdfafb5fbacfeef6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2401838
Commit-Queue: Yue Zhang <yuezhanggg@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806220}
parent bc5b38db
...@@ -281,8 +281,6 @@ Still reading? ...@@ -281,8 +281,6 @@ Still reading?
<ignore regexp="The resource `R.string.accessibility_close_tab_group_button` appears to be unused"/> <ignore regexp="The resource `R.string.accessibility_close_tab_group_button` appears to be unused"/>
<ignore regexp="The resource `R.string.accessibility_close_tab_group_button_with_group_name` appears to be unused"/> <ignore regexp="The resource `R.string.accessibility_close_tab_group_button_with_group_name` appears to be unused"/>
<ignore regexp="The resource `R.string.accessibility_expand_tab_group_with_group_name` appears to be unused"/> <ignore regexp="The resource `R.string.accessibility_expand_tab_group_with_group_name` appears to be unused"/>
<ignore regexp="The resource `R.plurals.accessibility_dialog_back_button` appears to be unused"/>
<ignore regexp="The resource `R.plurals.accessibility_dialog_back_button_with_group_name` appears to be unused"/>
<ignore regexp="The resource `R.string.tab_grid_dialog_toolbar_edit_group_name` appears to be unused"/> <ignore regexp="The resource `R.string.tab_grid_dialog_toolbar_edit_group_name` appears to be unused"/>
<!-- Old-style and new-style WebAPKs use same resources for simplicity. Old-style WebAPKs do <!-- Old-style and new-style WebAPKs use same resources for simplicity. Old-style WebAPKs do
not use R.style.SplashTheme but new-style WebAPKs do. not use R.style.SplashTheme but new-style WebAPKs do.
......
...@@ -371,10 +371,22 @@ public class TabGridDialogMediator implements SnackbarManager.SnackbarController ...@@ -371,10 +371,22 @@ public class TabGridDialogMediator implements SnackbarManager.SnackbarController
Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId); Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId);
String storedTitle = mTabGroupTitleEditor.getTabGroupTitle(getRootId(currentTab)); String storedTitle = mTabGroupTitleEditor.getTabGroupTitle(getRootId(currentTab));
if (storedTitle != null && relatedTabs.size() > 1) { if (storedTitle != null && relatedTabs.size() > 1) {
if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
mModel.set(TabGridPanelProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION,
mContext.getResources().getQuantityString(
R.plurals.accessibility_dialog_back_button_with_group_name,
relatedTabs.size(), storedTitle, relatedTabs.size()));
}
mModel.set(TabGridPanelProperties.HEADER_TITLE, storedTitle); mModel.set(TabGridPanelProperties.HEADER_TITLE, storedTitle);
return; return;
} }
} }
if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
mModel.set(TabGridPanelProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION,
mContext.getResources().getQuantityString(
R.plurals.accessibility_dialog_back_button, relatedTabs.size(),
relatedTabs.size()));
}
mModel.set(TabGridPanelProperties.HEADER_TITLE, mModel.set(TabGridPanelProperties.HEADER_TITLE,
mContext.getResources().getQuantityString( mContext.getResources().getQuantityString(
R.plurals.bottom_tab_grid_title_placeholder, tabsCount, tabsCount)); R.plurals.bottom_tab_grid_title_placeholder, tabsCount, tabsCount));
...@@ -511,12 +523,24 @@ public class TabGridDialogMediator implements SnackbarManager.SnackbarController ...@@ -511,12 +523,24 @@ public class TabGridDialogMediator implements SnackbarManager.SnackbarController
String originalTitle = mContext.getResources().getQuantityString( String originalTitle = mContext.getResources().getQuantityString(
R.plurals.bottom_tab_grid_title_placeholder, tabsCount, tabsCount); R.plurals.bottom_tab_grid_title_placeholder, tabsCount, tabsCount);
if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
mModel.set(TabGridPanelProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION,
mContext.getResources().getQuantityString(
R.plurals.accessibility_dialog_back_button, tabsCount, tabsCount));
}
mModel.set(TabGridPanelProperties.HEADER_TITLE, originalTitle); mModel.set(TabGridPanelProperties.HEADER_TITLE, originalTitle);
mTabGroupTitleEditor.updateTabGroupTitle(currentTab, originalTitle); mTabGroupTitleEditor.updateTabGroupTitle(currentTab, originalTitle);
return; return;
} }
mTabGroupTitleEditor.storeTabGroupTitle(getRootId(currentTab), mCurrentGroupModifiedTitle); mTabGroupTitleEditor.storeTabGroupTitle(getRootId(currentTab), mCurrentGroupModifiedTitle);
mTabGroupTitleEditor.updateTabGroupTitle(currentTab, mCurrentGroupModifiedTitle); mTabGroupTitleEditor.updateTabGroupTitle(currentTab, mCurrentGroupModifiedTitle);
if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
int relatedTabsCount = getRelatedTabs(mCurrentTabId).size();
mModel.set(TabGridPanelProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION,
mContext.getResources().getQuantityString(
R.plurals.accessibility_dialog_back_button_with_group_name,
relatedTabsCount, mCurrentGroupModifiedTitle, relatedTabsCount));
}
mModel.set(TabGridPanelProperties.HEADER_TITLE, mCurrentGroupModifiedTitle); mModel.set(TabGridPanelProperties.HEADER_TITLE, mCurrentGroupModifiedTitle);
RecordUserAction.record("TabGridDialog.TabGroupNamedInDialog"); RecordUserAction.record("TabGridDialog.TabGroupNamedInDialog");
mCurrentGroupModifiedTitle = null; mCurrentGroupModifiedTitle = null;
......
...@@ -70,6 +70,9 @@ class TabGridPanelProperties { ...@@ -70,6 +70,9 @@ class TabGridPanelProperties {
new PropertyModel.WritableBooleanPropertyKey(); new PropertyModel.WritableBooleanPropertyKey();
public static final PropertyModel.WritableBooleanPropertyKey IS_KEYBOARD_VISIBLE = public static final PropertyModel.WritableBooleanPropertyKey IS_KEYBOARD_VISIBLE =
new PropertyModel.WritableBooleanPropertyKey(); new PropertyModel.WritableBooleanPropertyKey();
public static final PropertyModel
.WritableObjectPropertyKey<String> COLLAPSE_BUTTON_CONTENT_DESCRIPTION =
new PropertyModel.WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COLLAPSE_CLICK_LISTENER, public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COLLAPSE_CLICK_LISTENER,
ADD_CLICK_LISTENER, HEADER_TITLE, CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT, ADD_CLICK_LISTENER, HEADER_TITLE, CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT,
IS_DIALOG_VISIBLE, SCRIMVIEW_CLICK_RUNNABLE, ANIMATION_SOURCE_VIEW, UNGROUP_BAR_STATUS, IS_DIALOG_VISIBLE, SCRIMVIEW_CLICK_RUNNABLE, ANIMATION_SOURCE_VIEW, UNGROUP_BAR_STATUS,
...@@ -77,5 +80,5 @@ class TabGridPanelProperties { ...@@ -77,5 +80,5 @@ class TabGridPanelProperties {
DIALOG_UNGROUP_BAR_HOVERED_BACKGROUND_COLOR_ID, DIALOG_UNGROUP_BAR_TEXT_APPEARANCE, DIALOG_UNGROUP_BAR_HOVERED_BACKGROUND_COLOR_ID, DIALOG_UNGROUP_BAR_TEXT_APPEARANCE,
INITIAL_SCROLL_INDEX, IS_MAIN_CONTENT_VISIBLE, MENU_CLICK_LISTENER, TITLE_TEXT_WATCHER, INITIAL_SCROLL_INDEX, IS_MAIN_CONTENT_VISIBLE, MENU_CLICK_LISTENER, TITLE_TEXT_WATCHER,
TITLE_TEXT_ON_FOCUS_LISTENER, TITLE_CURSOR_VISIBILITY, IS_TITLE_TEXT_FOCUSED, TITLE_TEXT_ON_FOCUS_LISTENER, TITLE_CURSOR_VISIBILITY, IS_TITLE_TEXT_FOCUSED,
IS_KEYBOARD_VISIBLE}; IS_KEYBOARD_VISIBLE, COLLAPSE_BUTTON_CONTENT_DESCRIPTION};
} }
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.tasks.tab_management; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.tasks.tab_management;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.ADD_CLICK_LISTENER; import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.ADD_CLICK_LISTENER;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.ANIMATION_SOURCE_VIEW; import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.ANIMATION_SOURCE_VIEW;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.COLLAPSE_BUTTON_CONTENT_DESCRIPTION;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.COLLAPSE_CLICK_LISTENER; import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.COLLAPSE_CLICK_LISTENER;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.CONTENT_TOP_MARGIN; import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.CONTENT_TOP_MARGIN;
import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.DIALOG_BACKGROUND_RESOURCE_ID; import static org.chromium.chrome.browser.tasks.tab_management.TabGridPanelProperties.DIALOG_BACKGROUND_RESOURCE_ID;
...@@ -139,6 +140,11 @@ class TabGridPanelViewBinder { ...@@ -139,6 +140,11 @@ class TabGridPanelViewBinder {
if (!model.get(IS_KEYBOARD_VISIBLE)) { if (!model.get(IS_KEYBOARD_VISIBLE)) {
viewHolder.toolbarView.hideKeyboard(); viewHolder.toolbarView.hideKeyboard();
} }
} else if (COLLAPSE_BUTTON_CONTENT_DESCRIPTION == propertyKey) {
if (TabUiFeatureUtilities.isLaunchPolishEnabled()) {
viewHolder.toolbarView.setLeftButtonContentDescription(
model.get(COLLAPSE_BUTTON_CONTENT_DESCRIPTION));
}
} }
} }
} }
...@@ -9,6 +9,7 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; ...@@ -9,6 +9,7 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.pressImeActionButton;
import static androidx.test.espresso.action.ViewActions.replaceText; import static androidx.test.espresso.action.ViewActions.replaceText;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.assertion.ViewAssertions.matches;
...@@ -109,6 +110,7 @@ import org.chromium.chrome.test.util.ChromeRenderTestRule; ...@@ -109,6 +110,7 @@ import org.chromium.chrome.test.util.ChromeRenderTestRule;
import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.test.util.NightModeTestUtils; import org.chromium.ui.test.util.NightModeTestUtils;
import org.chromium.ui.test.util.UiRestriction; import org.chromium.ui.test.util.UiRestriction;
import org.chromium.ui.util.ColorUtils; import org.chromium.ui.util.ColorUtils;
...@@ -129,6 +131,8 @@ public class TabGridDialogTest { ...@@ -129,6 +131,8 @@ public class TabGridDialogTest {
private static final String CUSTOMIZED_TITLE2 = "wfh funs"; private static final String CUSTOMIZED_TITLE2 = "wfh funs";
private static final String START_SURFACE_BASE_PARAMS = private static final String START_SURFACE_BASE_PARAMS =
"force-fieldtrial-params=Study.Group:start_surface_variation"; "force-fieldtrial-params=Study.Group:start_surface_variation";
private static final String TAB_GROUP_LAUNCH_POLISH_PARAMS =
"force-fieldtrial-params=Study.Group:enable_launch_polish/true";
private boolean mHasReceivedSourceRect; private boolean mHasReceivedSourceRect;
private TabSelectionEditorTestingRobot mSelectionEditorRobot = private TabSelectionEditorTestingRobot mSelectionEditorRobot =
...@@ -600,7 +604,7 @@ public class TabGridDialogTest { ...@@ -600,7 +604,7 @@ public class TabGridDialogTest {
openDialogFromTabSwitcherAndVerify(cta, 2, openDialogFromTabSwitcherAndVerify(cta, 2,
cta.getResources().getQuantityString( cta.getResources().getQuantityString(
R.plurals.bottom_tab_grid_title_placeholder, 2, 2)); R.plurals.bottom_tab_grid_title_placeholder, 2, 2));
editDialogTitle(CUSTOMIZED_TITLE1); editDialogTitle(cta, CUSTOMIZED_TITLE1);
// Verify the title is updated in both tab switcher and dialog. // Verify the title is updated in both tab switcher and dialog.
clickScrimToExitDialog(cta); clickScrimToExitDialog(cta);
...@@ -611,7 +615,7 @@ public class TabGridDialogTest { ...@@ -611,7 +615,7 @@ public class TabGridDialogTest {
// Modify title in dialog from tab strip. // Modify title in dialog from tab strip.
clickFirstTabInDialog(cta); clickFirstTabInDialog(cta);
openDialogFromStripAndVerify(cta, 2, CUSTOMIZED_TITLE1); openDialogFromStripAndVerify(cta, 2, CUSTOMIZED_TITLE1);
editDialogTitle(CUSTOMIZED_TITLE2); editDialogTitle(cta, CUSTOMIZED_TITLE2);
clickScrimToExitDialog(cta); clickScrimToExitDialog(cta);
waitForDialogHidingAnimation(cta); waitForDialogHidingAnimation(cta);
...@@ -760,8 +764,12 @@ public class TabGridDialogTest { ...@@ -760,8 +764,12 @@ public class TabGridDialogTest {
@Test @Test
@MediumTest @MediumTest
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID}) // clang-format off
@Features.EnableFeatures({ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID,
ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
@CommandLineFlags.Add({"force-fieldtrials=Study/Group", TAB_GROUP_LAUNCH_POLISH_PARAMS})
public void testAccessibilityString() throws ExecutionException { public void testAccessibilityString() throws ExecutionException {
// clang-format on
final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
createTabs(cta, false, 3); createTabs(cta, false, 3);
enterTabSwitcher(cta); enterTabSwitcher(cta);
...@@ -769,34 +777,56 @@ public class TabGridDialogTest { ...@@ -769,34 +777,56 @@ public class TabGridDialogTest {
mergeAllNormalTabsToAGroup(cta); mergeAllNormalTabsToAGroup(cta);
verifyTabSwitcherCardCount(cta, 1); verifyTabSwitcherCardCount(cta, 1);
// Verify the initial content description. // Verify the initial group card content description.
RecyclerView recyclerView = cta.findViewById(R.id.tab_list_view); RecyclerView recyclerView = cta.findViewById(R.id.tab_list_view);
View firstItem = recyclerView.findViewHolderForAdapterPosition(0).itemView; View firstItem = recyclerView.findViewHolderForAdapterPosition(0).itemView;
String targetString = "Expand tab group with 3 tabs."; String expandTargetString = "Expand tab group with 3 tabs.";
assertEquals(targetString, firstItem.getContentDescription()); assertEquals(expandTargetString, firstItem.getContentDescription());
// Content description should update with group title. // Back button content description should update with group title.
String collapseTargetString = "Collapse tab group with 3 tabs.";
openDialogFromTabSwitcherAndVerify(cta, 3, null); openDialogFromTabSwitcherAndVerify(cta, 3, null);
editDialogTitle(CUSTOMIZED_TITLE1); verifyDialogBackButtonContentDescription(cta, collapseTargetString);
editDialogTitle(cta, CUSTOMIZED_TITLE1);
collapseTargetString =
String.format("Collapse %s tab group with 3 tabs.", CUSTOMIZED_TITLE1);
verifyDialogBackButtonContentDescription(cta, collapseTargetString);
// Group card content description should update with group title.
clickScrimToExitDialog(cta); clickScrimToExitDialog(cta);
waitForDialogHidingAnimationInTabSwitcher(cta); waitForDialogHidingAnimationInTabSwitcher(cta);
verifyFirstCardTitle(CUSTOMIZED_TITLE1); verifyFirstCardTitle(CUSTOMIZED_TITLE1);
targetString = String.format("Expand %s tab group with 3 tabs.", CUSTOMIZED_TITLE1); expandTargetString = String.format("Expand %s tab group with 3 tabs.", CUSTOMIZED_TITLE1);
assertEquals(targetString, firstItem.getContentDescription()); assertEquals(expandTargetString, firstItem.getContentDescription());
// Content description should update with group count change. // Back button content description should update with group count change.
openDialogFromTabSwitcherAndVerify(cta, 3, CUSTOMIZED_TITLE1); openDialogFromTabSwitcherAndVerify(cta, 3, CUSTOMIZED_TITLE1);
closeFirstTabInDialog(); closeFirstTabInDialog();
verifyShowingDialog(cta, 2, CUSTOMIZED_TITLE1); verifyShowingDialog(cta, 2, CUSTOMIZED_TITLE1);
collapseTargetString =
String.format("Collapse %s tab group with 2 tabs.", CUSTOMIZED_TITLE1);
verifyDialogBackButtonContentDescription(cta, collapseTargetString);
// Group card content description should update with group count change.
clickScrimToExitDialog(cta); clickScrimToExitDialog(cta);
waitForDialogHidingAnimationInTabSwitcher(cta); waitForDialogHidingAnimationInTabSwitcher(cta);
targetString = String.format("Expand %s tab group with 2 tabs.", CUSTOMIZED_TITLE1); expandTargetString = String.format("Expand %s tab group with 2 tabs.", CUSTOMIZED_TITLE1);
assertEquals(targetString, firstItem.getContentDescription()); assertEquals(expandTargetString, firstItem.getContentDescription());
// Content description should restore when the group becomes a single tab. // Back button content description should restore when the group loses customized title.
openDialogFromTabSwitcherAndVerify(cta, 2, CUSTOMIZED_TITLE1); openDialogFromTabSwitcherAndVerify(cta, 2, CUSTOMIZED_TITLE1);
editDialogTitle(cta, "");
verifyShowingDialog(cta, 2, null);
collapseTargetString = "Collapse tab group with 2 tabs.";
verifyDialogBackButtonContentDescription(cta, collapseTargetString);
// Back button content description should update when the group becomes a single tab.
closeFirstTabInDialog(); closeFirstTabInDialog();
verifyShowingDialog(cta, 1, "1 tab"); verifyShowingDialog(cta, 1, "1 tab");
collapseTargetString = "Collapse 1 tab.";
verifyDialogBackButtonContentDescription(cta, collapseTargetString);
// Group card content description should restore when the group becomes a single tab.
clickScrimToExitDialog(cta); clickScrimToExitDialog(cta);
waitForDialogHidingAnimationInTabSwitcher(cta); waitForDialogHidingAnimationInTabSwitcher(cta);
assertEquals(null, firstItem.getContentDescription()); assertEquals(null, firstItem.getContentDescription());
...@@ -1097,7 +1127,7 @@ public class TabGridDialogTest { ...@@ -1097,7 +1127,7 @@ public class TabGridDialogTest {
}); });
} }
private void editDialogTitle(String title) { private void editDialogTitle(ChromeTabbedActivity cta, String title) {
onView(allOf(withParent(withId(R.id.main_content)), withId(R.id.title))) onView(allOf(withParent(withId(R.id.main_content)), withId(R.id.title)))
.perform(click()) .perform(click())
.check((v, e) -> { .check((v, e) -> {
...@@ -1106,7 +1136,12 @@ public class TabGridDialogTest { ...@@ -1106,7 +1136,12 @@ public class TabGridDialogTest {
assertEquals(titleView.getText().length(), assertEquals(titleView.getText().length(),
titleView.getSelectionEnd() - titleView.getSelectionStart()); titleView.getSelectionEnd() - titleView.getSelectionStart());
}) })
.perform(replaceText(title)); .perform(replaceText(title))
.perform(pressImeActionButton());
// Wait until the keyboard is hidden to make sure the edit has taken effect.
KeyboardVisibilityDelegate delegate = KeyboardVisibilityDelegate.getInstance();
CriteriaHelper.pollUiThread(
() -> !delegate.isKeyboardShowing(cta, cta.getCompositorViewHolder()));
} }
private void verifyFirstCardTitle(String title) { private void verifyFirstCardTitle(String title) {
...@@ -1162,4 +1197,11 @@ public class TabGridDialogTest { ...@@ -1162,4 +1197,11 @@ public class TabGridDialogTest {
isDisplayed())) isDisplayed()))
.perform(click()); .perform(click());
} }
private void verifyDialogBackButtonContentDescription(ChromeTabbedActivity cta, String s) {
assertTrue(isDialogShowing(cta));
onView(allOf(withId(R.id.toolbar_left_button),
isDescendantOfA(withId(R.id.dialog_container_view))))
.check((v, e) -> assertEquals(s, v.getContentDescription()));
}
} }
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