Skip to content

Commit

Permalink
[CleanUp][TextField] Centralize accessibility event dispatching logic…
Browse files Browse the repository at this point in the history
… for drop-downs

This also fixes the bug that clients cannot use custom AccessibilityDelegate with drop-down mode.

PiperOrigin-RevId: 447748501
  • Loading branch information
drchen authored and leticiarossi committed May 10, 2022
1 parent 72228f4 commit d3ab6d7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 34 deletions.
Expand Up @@ -47,7 +47,6 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.animation.AnimationUtils;
import com.google.android.material.textfield.TextInputLayout.AccessibilityDelegate;
import com.google.android.material.textfield.TextInputLayout.BoxBackgroundMode;

/** Default initialization of the exposed dropdown menu {@link TextInputLayout.EndIconMode}. */
Expand All @@ -62,37 +61,6 @@ class DropdownMenuEndIconDelegate extends EndIconDelegate {
@Nullable
private AutoCompleteTextView autoCompleteTextView;

private final TextInputLayout.AccessibilityDelegate accessibilityDelegate =
new AccessibilityDelegate(textInputLayout) {
@Override
public void onInitializeAccessibilityNodeInfo(
View host, @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
// The non-editable exposed dropdown menu behaves like a Spinner.
if (!isEditable(autoCompleteTextView)) {
info.setClassName(Spinner.class.getName());
}
if (info.isShowingHintText()) {
// Set hint text to null so TalkBack doesn't announce the label twice when there is no
// item selected.
info.setHintText(null);
}
}

@Override
public void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(host, event);
// If dropdown is non editable, layout click is what triggers showing/hiding the popup
// list. Otherwise, arrow icon alone is what triggers it.
if (event.getEventType() == TYPE_VIEW_CLICKED
&& accessibilityManager.isEnabled()
&& !isEditable(autoCompleteTextView)) {
showHideDropdown();
updateDropdownPopupDirty();
}
}
};

private final OnClickListener onIconClickListener = view -> showHideDropdown();

private final OnFocusChangeListener onEditTextFocusChangeListener = (view, hasFocus) -> {
Expand Down Expand Up @@ -177,8 +145,6 @@ public void onEditTextAttached(@Nullable EditText editText) {
if (!isEditable(editText) && accessibilityManager.isTouchExplorationEnabled()) {
ViewCompat.setImportantForAccessibility(endIconView, IMPORTANT_FOR_ACCESSIBILITY_NO);
}
textInputLayout.setTextInputAccessibilityDelegate(accessibilityDelegate);

textInputLayout.setEndIconVisible(true);
}

Expand All @@ -202,6 +168,32 @@ OnFocusChangeListener getOnEditTextFocusChangeListener() {
return onEditTextFocusChangeListener;
}

@Override
public void onInitializeAccessibilityNodeInfo(
View host, @NonNull AccessibilityNodeInfoCompat info) {
// The non-editable exposed dropdown menu behaves like a Spinner.
if (!isEditable(autoCompleteTextView)) {
info.setClassName(Spinner.class.getName());
}
if (info.isShowingHintText()) {
// Set hint text to null so TalkBack doesn't announce the label twice when there is no
// item selected.
info.setHintText(null);
}
}

@Override
public void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent event) {
// If dropdown is non editable, layout click is what triggers showing/hiding the popup
// list. Otherwise, arrow icon alone is what triggers it.
if (event.getEventType() == TYPE_VIEW_CLICKED
&& accessibilityManager.isEnabled()
&& !isEditable(autoCompleteTextView)) {
showHideDropdown();
updateDropdownPopupDirty();
}
}

private void showHideDropdown() {
if (autoCompleteTextView == null) {
return;
Expand Down
Expand Up @@ -18,12 +18,15 @@

import android.content.Context;
import android.text.Editable;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.EditText;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.internal.CheckableImageButton;
import com.google.android.material.textfield.TextInputLayout.BoxBackgroundMode;

Expand Down Expand Up @@ -122,4 +125,17 @@ void beforeEditTextChanged(CharSequence s, int start, int count, int after) {}
* @see android.text.TextWatcher#afterTextChanged(Editable)
*/
void afterEditTextChanged(Editable s) {}

/**
* This method will be called when the associated {@link TextInputLayout} is initializing the
* accessibility node info.
*/
void onInitializeAccessibilityNodeInfo(View host, @NonNull AccessibilityNodeInfoCompat info) {}

/**
* This method will be called when the associated {@link TextInputLayout} is populating a
* accessibility event.
*/
void onPopulateAccessibilityEvent(View host, @NonNull AccessibilityEvent event) {}
}

Expand Up @@ -4239,6 +4239,15 @@ public void onInitializeAccessibilityNodeInfo(
info.setLabelFor(helperTextView);
}
}

layout.endLayout.getEndIconDelegate().onInitializeAccessibilityNodeInfo(host, info);
}

@Override
public void onPopulateAccessibilityEvent(
@NonNull View host, @NonNull AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(host, event);
layout.endLayout.getEndIconDelegate().onPopulateAccessibilityEvent(host, event);
}
}
}

0 comments on commit d3ab6d7

Please sign in to comment.