From a0180c4b10c760037da8e8a9affaed57e414c92e Mon Sep 17 00:00:00 2001 From: Dan Nizri Date: Wed, 25 May 2022 16:25:09 -0400 Subject: [PATCH 1/2] [TextInputLayout] Fix for TextInputLayout leak via AccessibilityManager. --- .../DropdownMenuEndIconDelegate.java | 66 ++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java index 011f5553cfb..c95a527a34d 100644 --- a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java +++ b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java @@ -40,12 +40,12 @@ import android.text.TextWatcher; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.View.OnTouchListener; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener; import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView.OnDismissListener; import android.widget.EditText; @@ -54,6 +54,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityManagerCompat; +import androidx.core.view.accessibility.AccessibilityManagerCompat.TouchExplorationStateChangeListener; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import com.google.android.material.animation.AnimationUtils; import com.google.android.material.color.MaterialColors; @@ -190,6 +192,36 @@ public void run() { editText.setOnDismissListener(null); } } + textInputLayout.removeOnAttachStateChangeListener(onAttachStateChangeListener); + removeTouchExplorationStateChangeListenerIfNeeded(); + } + }; + + private final OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View ignored) { + addTouchExplorationStateChangeListenerIfNeeded(); + } + + @Override + public void onViewDetachedFromWindow(View ignored) { + removeTouchExplorationStateChangeListenerIfNeeded(); + } + }; + + private final TouchExplorationStateChangeListener touchExplorationStateChangeListener = + new TouchExplorationStateChangeListener() { + @Override + public void onTouchExplorationStateChanged(boolean enabled) { + if (textInputLayout != null) { + final AutoCompleteTextView autoCompleteTextView = + (AutoCompleteTextView) textInputLayout.getEditText(); + if (autoCompleteTextView != null && !isEditable(autoCompleteTextView)) { + ViewCompat.setImportantForAccessibility( + endIconView, + enabled ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_YES); + } + } } }; @@ -265,20 +297,8 @@ public void onClick(View v) { initAnimators(); accessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); - if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { - accessibilityManager.addTouchExplorationStateChangeListener( - new TouchExplorationStateChangeListener() { - @Override - public void onTouchExplorationStateChanged(boolean enabled) { - if (textInputLayout.getEditText() != null - && !isEditable(textInputLayout.getEditText())) { - ViewCompat.setImportantForAccessibility( - endIconView, - enabled ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_YES); - } - } - }); - } + textInputLayout.addOnAttachStateChangeListener(onAttachStateChangeListener); + addTouchExplorationStateChangeListenerIfNeeded(); } @Override @@ -530,4 +550,20 @@ public void onAnimationUpdate(@NonNull ValueAnimator animation) { return animator; } + + private void addTouchExplorationStateChangeListenerIfNeeded() { + if (accessibilityManager != null + && textInputLayout != null + && ViewCompat.isAttachedToWindow(textInputLayout)) { + AccessibilityManagerCompat.addTouchExplorationStateChangeListener( + accessibilityManager, touchExplorationStateChangeListener); + } + } + + private void removeTouchExplorationStateChangeListenerIfNeeded() { + if (accessibilityManager != null) { + AccessibilityManagerCompat.removeTouchExplorationStateChangeListener( + accessibilityManager, touchExplorationStateChangeListener); + } + } } From 5291336c066ca26cfbae04d84cc0ef87573c1e33 Mon Sep 17 00:00:00 2001 From: Dan Nizri Date: Wed, 25 May 2022 16:53:17 -0400 Subject: [PATCH 2/2] Add check for previous end icon mode --- .../material/textfield/DropdownMenuEndIconDelegate.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java index c95a527a34d..45c625725f9 100644 --- a/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java +++ b/lib/java/com/google/android/material/textfield/DropdownMenuEndIconDelegate.java @@ -192,8 +192,10 @@ public void run() { editText.setOnDismissListener(null); } } - textInputLayout.removeOnAttachStateChangeListener(onAttachStateChangeListener); - removeTouchExplorationStateChangeListenerIfNeeded(); + if (previousIcon == TextInputLayout.END_ICON_DROPDOWN_MENU) { + textInputLayout.removeOnAttachStateChangeListener(onAttachStateChangeListener); + removeTouchExplorationStateChangeListenerIfNeeded(); + } } };