Mark actions unimportant for accessibility when obscured by RemoteInputView.
To prevent animation jank, we don't actually hide or remove the action buttons (like "reply") on a notification while they're obscured by the remote input view (like while the user is typing their reply). Visually, this is fine, but Voice Access still sees those buttons as important. If the user speaks the action (like "reply"), Voice Access will activate the buttons, but nothing visible will happen. If the user has numbers shown, Voice Access will number the buttons, but the numbers will appear to point at the remote input view instead. To avoid this, we mark the NotificationActionListLayout as unimportant for accessibility while the remote input view is visible, and return it to "auto" important when the remote input view isn't visible. Bug: 230297786 Test: atest NotificationContentViewTest Change-Id: I50fdda3adabd0039ed84b3781c0d275fc176ef89
This commit is contained in:
@@ -1986,6 +1986,25 @@ public class NotificationContentView extends FrameLayout implements Notification
|
||||
public void setRemoteInputVisible(boolean remoteInputVisible) {
|
||||
mRemoteInputVisible = remoteInputVisible;
|
||||
setClipChildren(!remoteInputVisible);
|
||||
setActionsImportanceForAccessibility(
|
||||
remoteInputVisible ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
}
|
||||
|
||||
private void setActionsImportanceForAccessibility(int mode) {
|
||||
if (mExpandedChild != null) {
|
||||
setActionsImportanceForAccessibility(mode, mExpandedChild);
|
||||
}
|
||||
if (mHeadsUpChild != null) {
|
||||
setActionsImportanceForAccessibility(mode, mHeadsUpChild);
|
||||
}
|
||||
}
|
||||
|
||||
private void setActionsImportanceForAccessibility(int mode, View child) {
|
||||
View actionsCandidate = child.findViewById(com.android.internal.R.id.actions);
|
||||
if (actionsCandidate != null) {
|
||||
actionsCandidate.setImportantForAccessibility(mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.widget.NotificationActionListLayout;
|
||||
import com.android.internal.widget.NotificationExpandButton;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
|
||||
@@ -142,4 +143,60 @@ public class NotificationContentViewTest extends SysuiTestCase {
|
||||
verify(mockExpandedEB, times(1)).requestAccessibilityFocus();
|
||||
verify(mockHeadsUpEB, times(0)).requestAccessibilityFocus();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testRemoteInputVisibleSetsActionsUnimportantHideDescendantsForAccessibility() {
|
||||
View mockContracted = mock(NotificationHeaderView.class);
|
||||
|
||||
View mockExpandedActions = mock(NotificationActionListLayout.class);
|
||||
View mockExpanded = mock(NotificationHeaderView.class);
|
||||
when(mockExpanded.findViewById(com.android.internal.R.id.actions)).thenReturn(
|
||||
mockExpandedActions);
|
||||
|
||||
View mockHeadsUpActions = mock(NotificationActionListLayout.class);
|
||||
View mockHeadsUp = mock(NotificationHeaderView.class);
|
||||
when(mockHeadsUp.findViewById(com.android.internal.R.id.actions)).thenReturn(
|
||||
mockHeadsUpActions);
|
||||
|
||||
mView.setContractedChild(mockContracted);
|
||||
mView.setExpandedChild(mockExpanded);
|
||||
mView.setHeadsUpChild(mockHeadsUp);
|
||||
|
||||
mView.setRemoteInputVisible(true);
|
||||
|
||||
verify(mockContracted, times(0)).findViewById(0);
|
||||
verify(mockExpandedActions, times(1)).setImportantForAccessibility(
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
verify(mockHeadsUpActions, times(1)).setImportantForAccessibility(
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testRemoteInputInvisibleSetsActionsAutoImportantForAccessibility() {
|
||||
View mockContracted = mock(NotificationHeaderView.class);
|
||||
|
||||
View mockExpandedActions = mock(NotificationActionListLayout.class);
|
||||
View mockExpanded = mock(NotificationHeaderView.class);
|
||||
when(mockExpanded.findViewById(com.android.internal.R.id.actions)).thenReturn(
|
||||
mockExpandedActions);
|
||||
|
||||
View mockHeadsUpActions = mock(NotificationActionListLayout.class);
|
||||
View mockHeadsUp = mock(NotificationHeaderView.class);
|
||||
when(mockHeadsUp.findViewById(com.android.internal.R.id.actions)).thenReturn(
|
||||
mockHeadsUpActions);
|
||||
|
||||
mView.setContractedChild(mockContracted);
|
||||
mView.setExpandedChild(mockExpanded);
|
||||
mView.setHeadsUpChild(mockHeadsUp);
|
||||
|
||||
mView.setRemoteInputVisible(false);
|
||||
|
||||
verify(mockContracted, times(0)).findViewById(0);
|
||||
verify(mockExpandedActions, times(1)).setImportantForAccessibility(
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
verify(mockHeadsUpActions, times(1)).setImportantForAccessibility(
|
||||
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user