From 473fc125fa2d2ebebac797d01eeee6c15cc05a56 Mon Sep 17 00:00:00 2001 From: yingleiw Date: Fri, 11 Oct 2019 15:10:55 -0700 Subject: [PATCH] change CompoundButton button to use the accessibility state API Test: tested with updated talkback (see CL/274237446). It works as before. Change-Id: I928350c8bc9112252e8b8a703c465489f03fde11 --- core/java/android/widget/CompoundButton.java | 44 +++++++++++++++++++- core/java/android/widget/Switch.java | 35 ++++++++-------- core/java/android/widget/ToggleButton.java | 20 +++++++++ core/res/res/values/strings.xml | 5 +++ core/res/res/values/symbols.xml | 4 ++ 5 files changed, 88 insertions(+), 20 deletions(-) diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 2674ca4d159aa..547aad64fc3e5 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -80,6 +80,8 @@ public abstract class CompoundButton extends Button implements Checkable { // to sanitize autofill requests. private boolean mCheckedFromResource = false; + private CharSequence mCustomStateDescription = null; + private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; @@ -156,6 +158,44 @@ public abstract class CompoundButton extends Button implements Checkable { return mChecked; } + /** @hide */ + @NonNull + protected CharSequence getButtonStateDescription() { + if (isChecked()) { + return getResources().getString(R.string.checked); + } else { + return getResources().getString(R.string.not_checked); + } + } + + /** + * This function is called when an instance or subclass sets the state description. Once this + * is called and the argument is not null, the app developer will be responsible for updating + * state description when checked state changes and we will not set state description + * in {@link #setChecked}. App developers can restore the default behavior by setting the + * argument to null. If {@link #setChecked} is called first and then setStateDescription is + * called, two state change events will be merged by event throttling and we can still get + * the correct state description. + * + * @param stateDescription The state description. + */ + @Override + public void setStateDescription(@Nullable CharSequence stateDescription) { + mCustomStateDescription = stateDescription; + if (stateDescription == null) { + setDefaultStateDescritption(); + } else { + super.setStateDescription(stateDescription); + } + } + + /** @hide **/ + protected void setDefaultStateDescritption() { + if (mCustomStateDescription == null) { + super.setStateDescription(getButtonStateDescription()); + } + } + /** *

Changes the checked state of this button.

* @@ -167,8 +207,6 @@ public abstract class CompoundButton extends Button implements Checkable { mCheckedFromResource = false; mChecked = checked; refreshDrawableState(); - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting) { @@ -189,6 +227,8 @@ public abstract class CompoundButton extends Button implements Checkable { mBroadcasting = false; } + // setStateDescription will not send out event if the description is unchanged. + setDefaultStateDescritption(); } /** diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index d57b3bc7ad3b4..ac2336c4a10f5 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -52,7 +52,6 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.ViewStructure; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import com.android.internal.R; @@ -852,6 +851,9 @@ public class Switch extends CompoundButton { public void setTextOn(CharSequence textOn) { mTextOn = textOn; requestLayout(); + // Default state is derived from on/off-text, so state has to be updated when on/off-text + // are updated. + setDefaultStateDescritption(); } /** @@ -872,6 +874,9 @@ public class Switch extends CompoundButton { public void setTextOff(CharSequence textOff) { mTextOff = textOff; requestLayout(); + // Default state is derived from on/off-text, so state has to be updated when on/off-text + // are updated. + setDefaultStateDescritption(); } /** @@ -1161,6 +1166,17 @@ public class Switch extends CompoundButton { setChecked(!isChecked()); } + /** @hide **/ + @Override + @NonNull + protected CharSequence getButtonStateDescription() { + if (isChecked()) { + return mTextOn == null ? getResources().getString(R.string.capital_on) : mTextOn; + } else { + return mTextOff == null ? getResources().getString(R.string.capital_off) : mTextOff; + } + } + @Override public void setChecked(boolean checked) { super.setChecked(checked); @@ -1514,23 +1530,6 @@ public class Switch extends CompoundButton { } } - /** @hide */ - @Override - public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfoInternal(info); - CharSequence switchText = isChecked() ? mTextOn : mTextOff; - if (!TextUtils.isEmpty(switchText)) { - CharSequence oldText = info.getText(); - if (TextUtils.isEmpty(oldText)) { - info.setText(switchText); - } else { - StringBuilder newText = new StringBuilder(); - newText.append(oldText).append(' ').append(switchText); - info.setText(newText); - } - } - } - private static final FloatProperty THUMB_POS = new FloatProperty("thumbPos") { @Override public Float get(Switch object) { diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java index 9255ccb5cda73..d47405b3b11ec 100644 --- a/core/java/android/widget/ToggleButton.java +++ b/core/java/android/widget/ToggleButton.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.FloatRange; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; @@ -24,6 +25,8 @@ import android.graphics.drawable.LayerDrawable; import android.util.AttributeSet; import android.view.inspector.InspectableProperty; +import com.android.internal.R; + /** * Displays checked/unchecked states as a button * with a "light" indicator and by default accompanied with the text "ON" or "OFF". @@ -103,6 +106,9 @@ public class ToggleButton extends CompoundButton { */ public void setTextOn(CharSequence textOn) { mTextOn = textOn; + // Default state is derived from on/off-text, so state has to be updated when on/off-text + // are updated. + setDefaultStateDescritption(); } /** @@ -122,6 +128,9 @@ public class ToggleButton extends CompoundButton { */ public void setTextOff(CharSequence textOff) { mTextOff = textOff; + // Default state is derived from on/off-text, so state has to be updated when on/off-text + // are updated. + setDefaultStateDescritption(); } /** @@ -172,4 +181,15 @@ public class ToggleButton extends CompoundButton { public CharSequence getAccessibilityClassName() { return ToggleButton.class.getName(); } + + /** @hide **/ + @Override + @NonNull + protected CharSequence getButtonStateDescription() { + if (isChecked()) { + return mTextOn == null ? getResources().getString(R.string.capital_on) : mTextOn; + } else { + return mTextOff == null ? getResources().getString(R.string.capital_off) : mTextOff; + } + } } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0b198a7cc7fdc..a5bbab6cd8735 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3042,6 +3042,11 @@ OFF + + checked + + not checked + Complete action using