diff --git a/docs/components/TextField.md b/docs/components/TextField.md
index 44f0bce806c..396b32140fd 100644
--- a/docs/components/TextField.md
+++ b/docs/components/TextField.md
@@ -526,20 +526,21 @@ Element | Attribute | Related method(s)
#### Helper/error/counter text attributes
-Element | Attribute | Related method(s) | Default value
---------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------
-**Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled`
`isHelperTextEnabled` | `false`
-**Helper text** | `app:helperText` | `setHelperText`
`getHelperText` | `null`
-**Helper text color** | `app:helperTextColor` | `setHelperTextColor`
`getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall`
-**Error text enabled** | `app:errorEnabled` | `setErrorEnabled`
`isErrorEnabled` | `false`
-**Error text** | N/A | `setError`
`getError` | `null`
-**Error text color** | `app:errorTextColor` | `setErrorTextColor`
`getErrorCurrentTextColors` | `?attr/colorError`
-**Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall`
-**Counter text enabled** | `app:counterEnabled` | `setCounterEnabled`
`isCounterEnabled` | `false`
-**Counter text length** | `app:counterMaxLength` | `setCounterMaxLength`
`getCounterMaxLength` | `-1`
-**Counter text typography** | `app:counterTextAppearance`
`app:counterOverflowTextAppearance` | `setCounterTextAppearance`
`setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall`
-**Counter text color** | `app:counterTextColor`
`app:counterOverflowTextColor` | `setCounterTextColor`
`setCounterOverflowTextColor`
`getCounterTextColor`
`getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
`?attr/colorError` (`app:counterOverflowTextColor`)
+| Element | Attribute | Related method(s) | Default value |
+|------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled`
`isHelperTextEnabled` | `false` |
+| **Helper text** | `app:helperText` | `setHelperText`
`getHelperText` | `null` |
+| **Helper text color** | `app:helperTextColor` | `setHelperTextColor`
`getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) |
+| **Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Error text enabled** | `app:errorEnabled` | `setErrorEnabled`
`isErrorEnabled` | `false` |
+| **Error text** | N/A | `setError`
`getError` | `null` |
+| **Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion`
`getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE` |
+| **Error text color** | `app:errorTextColor` | `setErrorTextColor`
`getErrorCurrentTextColors` | `?attr/colorError` |
+| **Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Counter text enabled** | `app:counterEnabled` | `setCounterEnabled`
`isCounterEnabled` | `false` |
+| **Counter text length** | `app:counterMaxLength` | `setCounterMaxLength`
`getCounterMaxLength` | `-1` |
+| **Counter text typography** | `app:counterTextAppearance`
`app:counterOverflowTextAppearance` | `setCounterTextAppearance`
`setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Counter text color** | `app:counterTextColor`
`app:counterOverflowTextColor` | `setCounterTextColor`
`setCounterOverflowTextColor`
`getCounterTextColor`
`getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
`?attr/colorError` (`app:counterOverflowTextColor`) |
#### Prefix/suffix attributes
@@ -697,20 +698,21 @@ Element | Attribute | Related
#### Helper/error/counter text attributes
-Element | Attribute | Related method(s) | Default value
---------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------
-**Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled`
`isHelperTextEnabled` | `false`
-**Helper text** | `app:helperText` | `setHelperText`
`getHelperText` | `null`
-**Helper text color** | `app:helperTextColor` | `setHelperTextColor`
`getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
-**Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall`
-**Error text enabled** | `app:errorEnabled` | `setErrorEnabled`
`isErrorEnabled` | `false`
-**Error text** | N/A | `setError`
`getError` | `null`
-**Error text color** | `app:errorTextColor` | `setErrorTextColor`
`getErrorCurrentTextColors` | `?attr/colorError`
-**Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall`
-**Counter text enabled** | `app:counterEnabled` | `setCounterEnabled`
`isCounterEnabled` | `false`
-**Counter text length** | `app:counterMaxLength` | `setCounterMaxLength`
`getCounterMaxLength` | `-1`
-**Counter text typography** | `app:counterTextAppearance`
`app:counterOverflowTextAppearance` | `setCounterTextAppearance`
`setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall`
-**Counter text color** | `app:counterTextColor`
`app:counterOverflowTextColor` | `setCounterTextColor`
`setCounterOverflowTextColor`
`getCounterTextColor`
`getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
`?attr/colorError` (`app:counterOverflowTextColor`)
+| Element | Attribute | Related method(s) | Default value |
+|------------------------------------------|---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Helper text enabled** | `app:helperTextEnabled` | `setHelperTextEnabled`
`isHelperTextEnabled` | `false` |
+| **Helper text** | `app:helperText` | `setHelperText`
`getHelperText` | `null` |
+| **Helper text color** | `app:helperTextColor` | `setHelperTextColor`
`getHelperTextColor` | `?attr/colorOnSurfaceVariant` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml)) |
+| **Helper text typography** | `app:helperTextAppearance` | `setHelperTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Error text enabled** | `app:errorEnabled` | `setErrorEnabled`
`isErrorEnabled` | `false` |
+| **Error text** | N/A | `setError`
`getError` | `null` |
+| **Error text accessibility live region** | `app:errorAccessibilityLiveRegion` | `setErrorAccessibilityLiveRegion`
`getErrorAccessibilityLiveRegion` | `ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE` |
+| **Error text color** | `app:errorTextColor` | `setErrorTextColor`
`getErrorCurrentTextColors` | `?attr/colorError` |
+| **Error text typography** | `app:errorTextAppearance` | `setErrorTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Counter text enabled** | `app:counterEnabled` | `setCounterEnabled`
`isCounterEnabled` | `false` |
+| **Counter text length** | `app:counterMaxLength` | `setCounterMaxLength`
`getCounterMaxLength` | `-1` |
+| **Counter text typography** | `app:counterTextAppearance`
`app:counterOverflowTextAppearance` | `setCounterTextAppearance`
`setCounterOverflowTextAppearance` | `?attr/textAppearanceBodySmall` |
+| **Counter text color** | `app:counterTextColor`
`app:counterOverflowTextColor` | `setCounterTextColor`
`setCounterOverflowTextColor`
`getCounterTextColor`
`getCounterOverflowTextColor` | `?attr/colorOnSurfaceVariant` (`app:counterTextColor`) (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/textfield/res/color/m3_textfield_indicator_text_color.xml))
`?attr/colorError` (`app:counterOverflowTextColor`) |
#### Prefix/suffix attributes
diff --git a/lib/java/com/google/android/material/datepicker/DateSelector.java b/lib/java/com/google/android/material/datepicker/DateSelector.java
index 6c7a6ef7699..5f473b1fae4 100644
--- a/lib/java/com/google/android/material/datepicker/DateSelector.java
+++ b/lib/java/com/google/android/material/datepicker/DateSelector.java
@@ -100,6 +100,9 @@ public interface DateSelector extends Parcelable {
@NonNull
String getSelectionContentDescription(@NonNull Context context);
+ @Nullable
+ String getError();
+
@StringRes
int getDefaultTitleResId();
diff --git a/lib/java/com/google/android/material/datepicker/MaterialDatePicker.java b/lib/java/com/google/android/material/datepicker/MaterialDatePicker.java
index cf5085deaee..6281f4b9a3d 100644
--- a/lib/java/com/google/android/material/datepicker/MaterialDatePicker.java
+++ b/lib/java/com/google/android/material/datepicker/MaterialDatePicker.java
@@ -52,9 +52,11 @@
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
import androidx.core.util.Pair;
+import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.google.android.material.dialog.InsetDialogOnTouchListener;
import com.google.android.material.internal.CheckableImageButton;
import com.google.android.material.internal.EdgeToEdgeUtils;
@@ -296,6 +298,16 @@ public void onClick(View v) {
dismiss();
}
});
+ ViewCompat.setAccessibilityDelegate(
+ confirmButton,
+ new AccessibilityDelegateCompat() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setContentDescription(getDateSelector().getError());
+ }
+ });
Button cancelButton = root.findViewById(R.id.cancel_button);
cancelButton.setTag(CANCEL_BUTTON_TAG);
diff --git a/lib/java/com/google/android/material/datepicker/RangeDateSelector.java b/lib/java/com/google/android/material/datepicker/RangeDateSelector.java
index 637488cce1d..69db6c4512f 100644
--- a/lib/java/com/google/android/material/datepicker/RangeDateSelector.java
+++ b/lib/java/com/google/android/material/datepicker/RangeDateSelector.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputType;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,6 +35,7 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.core.util.Pair;
import androidx.core.util.Preconditions;
+import androidx.core.view.ViewCompat;
import com.google.android.material.internal.ManufacturerUtils;
import com.google.android.material.resources.MaterialAttributes;
import com.google.android.material.textfield.TextInputLayout;
@@ -50,6 +52,7 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public class RangeDateSelector implements DateSelector> {
+ @Nullable private CharSequence error;
private String invalidRangeStartError;
// "" is not considered an error
private final String invalidRangeEndError = " ";
@@ -176,6 +179,12 @@ public String getSelectionContentDescription(@NonNull Context context) {
R.string.mtrl_picker_announce_current_range_selection, startPlaceholder, endPlaceholder);
}
+ @Nullable
+ @Override
+ public String getError() {
+ return TextUtils.isEmpty(error) ? null : error.toString();
+ }
+
@Override
public int getDefaultTitleResId() {
return R.string.mtrl_picker_range_header_title;
@@ -199,6 +208,8 @@ public View onCreateTextInputView(
final TextInputLayout startTextInput =
root.findViewById(R.id.mtrl_picker_text_input_range_start);
final TextInputLayout endTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_end);
+ startTextInput.setErrorAccessibilityLiveRegion(ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE);
+ endTextInput.setErrorAccessibilityLiveRegion(ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE);
EditText startEditText = startTextInput.getEditText();
EditText endEditText = endTextInput.getEditText();
if (ManufacturerUtils.isDateInputKeyboardMissingSeparatorCharacters()) {
@@ -278,9 +289,7 @@ private void updateIfValidTextProposal(
if (proposedTextStart == null || proposedTextEnd == null) {
clearInvalidRange(startTextInput, endTextInput);
listener.onIncompleteSelectionChanged();
- return;
- }
- if (isValidRange(proposedTextStart, proposedTextEnd)) {
+ } else if (isValidRange(proposedTextStart, proposedTextEnd)) {
selectedStartItem = proposedTextStart;
selectedEndItem = proposedTextEnd;
listener.onSelectionChanged(getSelection());
@@ -288,6 +297,17 @@ private void updateIfValidTextProposal(
setInvalidRange(startTextInput, endTextInput);
listener.onIncompleteSelectionChanged();
}
+ updateError(startTextInput, endTextInput);
+ }
+
+ private void updateError(@NonNull TextInputLayout start, @NonNull TextInputLayout end) {
+ if (!TextUtils.isEmpty(start.getError())) {
+ error = start.getError();
+ } else if (!TextUtils.isEmpty(end.getError())) {
+ error = end.getError();
+ } else {
+ error = null;
+ }
}
private void clearInvalidRange(@NonNull TextInputLayout start, @NonNull TextInputLayout end) {
diff --git a/lib/java/com/google/android/material/datepicker/SingleDateSelector.java b/lib/java/com/google/android/material/datepicker/SingleDateSelector.java
index 618928e3076..145207d8024 100644
--- a/lib/java/com/google/android/material/datepicker/SingleDateSelector.java
+++ b/lib/java/com/google/android/material/datepicker/SingleDateSelector.java
@@ -23,6 +23,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.InputType;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -32,6 +33,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.core.util.Pair;
+import androidx.core.view.ViewCompat;
import com.google.android.material.internal.ManufacturerUtils;
import com.google.android.material.resources.MaterialAttributes;
import com.google.android.material.textfield.TextInputLayout;
@@ -47,6 +49,7 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public class SingleDateSelector implements DateSelector {
+ @Nullable private CharSequence error;
@Nullable private Long selectedItem;
@Nullable private SimpleDateFormat textInputFormat;
@@ -106,6 +109,7 @@ public View onCreateTextInputView(
View root = layoutInflater.inflate(R.layout.mtrl_picker_text_input_date, viewGroup, false);
TextInputLayout dateTextInput = root.findViewById(R.id.mtrl_picker_text_input_date);
+ dateTextInput.setErrorAccessibilityLiveRegion(ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE);
EditText dateEditText = dateTextInput.getEditText();
if (ManufacturerUtils.isDateInputKeyboardMissingSeparatorCharacters()) {
// Using the URI variation places the '/' and '.' in more prominent positions
@@ -135,11 +139,13 @@ void onValidDate(@Nullable Long day) {
} else {
select(day);
}
+ error = null;
listener.onSelectionChanged(getSelection());
}
@Override
void onInvalidDate() {
+ error = dateTextInput.getError();
listener.onIncompleteSelectionChanged();
}
});
@@ -177,6 +183,12 @@ public String getSelectionContentDescription(@NonNull Context context) {
return res.getString(R.string.mtrl_picker_announce_current_selection, placeholder);
}
+ @Nullable
+ @Override
+ public String getError() {
+ return TextUtils.isEmpty(error) ? null : error.toString();
+ }
+
@Override
public int getDefaultTitleResId() {
return R.string.mtrl_picker_date_header_title;
diff --git a/lib/java/com/google/android/material/textfield/IndicatorViewController.java b/lib/java/com/google/android/material/textfield/IndicatorViewController.java
index 856a24c440d..616fea8b178 100644
--- a/lib/java/com/google/android/material/textfield/IndicatorViewController.java
+++ b/lib/java/com/google/android/material/textfield/IndicatorViewController.java
@@ -119,6 +119,7 @@ final class IndicatorViewController {
private boolean errorEnabled;
@Nullable private TextView errorView;
@Nullable private CharSequence errorViewContentDescription;
+ private int errorViewAccessibilityLiveRegion;
private int errorTextAppearance;
@Nullable private ColorStateList errorViewTextColor;
@@ -501,8 +502,8 @@ void setErrorEnabled(boolean enabled) {
setErrorTextAppearance(errorTextAppearance);
setErrorViewTextColor(errorViewTextColor);
setErrorContentDescription(errorViewContentDescription);
+ setErrorAccessibilityLiveRegion(errorViewAccessibilityLiveRegion);
errorView.setVisibility(View.INVISIBLE);
- ViewCompat.setAccessibilityLiveRegion(errorView, ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
addIndicator(errorView, ERROR_INDEX);
} else {
hideError();
@@ -658,11 +659,22 @@ void setErrorContentDescription(@Nullable final CharSequence errorContentDescrip
}
}
+ void setErrorAccessibilityLiveRegion(final int accessibilityLiveRegion) {
+ this.errorViewAccessibilityLiveRegion = accessibilityLiveRegion;
+ if (errorView != null) {
+ ViewCompat.setAccessibilityLiveRegion(errorView, accessibilityLiveRegion);
+ }
+ }
+
@Nullable
CharSequence getErrorContentDescription() {
return errorViewContentDescription;
}
+ int getErrorAccessibilityLiveRegion() {
+ return errorViewAccessibilityLiveRegion;
+ }
+
@ColorInt
int getHelperTextViewCurrentTextColor() {
return helperTextView != null ? helperTextView.getCurrentTextColor() : -1;
diff --git a/lib/java/com/google/android/material/textfield/TextInputLayout.java b/lib/java/com/google/android/material/textfield/TextInputLayout.java
index cdd62fdaa1f..5ef2d0cee7a 100644
--- a/lib/java/com/google/android/material/textfield/TextInputLayout.java
+++ b/lib/java/com/google/android/material/textfield/TextInputLayout.java
@@ -614,6 +614,10 @@ public TextInputLayout(@NonNull Context context, @Nullable AttributeSet attrs, i
a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0);
final CharSequence errorContentDescription =
a.getText(R.styleable.TextInputLayout_errorContentDescription);
+ final int errorAccessibilityLiveRegion =
+ a.getInt(
+ R.styleable.TextInputLayout_errorAccessibilityLiveRegion,
+ ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false);
final int helperTextTextAppearance =
@@ -636,6 +640,7 @@ public TextInputLayout(@NonNull Context context, @Nullable AttributeSet attrs, i
a.getInt(R.styleable.TextInputLayout_boxBackgroundMode, BOX_BACKGROUND_NONE));
setErrorContentDescription(errorContentDescription);
+ setErrorAccessibilityLiveRegion(errorAccessibilityLiveRegion);
setCounterOverflowTextAppearance(counterOverflowTextAppearance);
setHelperTextTextAppearance(helperTextTextAppearance);
@@ -2071,6 +2076,25 @@ public CharSequence getErrorContentDescription() {
return indicatorViewController.getErrorContentDescription();
}
+ /**
+ * Sets an accessibility live region for the error message.
+ *
+ * @param errorAccessibilityLiveRegion Accessibility live region to set
+ * @attr ref com.google.android.material.R.styleable#TextInputLayout_errorAccessibilityLiveRegion
+ */
+ public void setErrorAccessibilityLiveRegion(final int errorAccessibilityLiveRegion) {
+ indicatorViewController.setErrorAccessibilityLiveRegion(errorAccessibilityLiveRegion);
+ }
+
+ /**
+ * Returns the accessibility live region of the error message.
+ *
+ * @see #setErrorAccessibilityLiveRegion(int)
+ */
+ public int getErrorAccessibilityLiveRegion() {
+ return indicatorViewController.getErrorAccessibilityLiveRegion();
+ }
+
/**
* Sets an error message that will be displayed below our {@link EditText}. If the {@code error}
* is {@code null}, the error message will be cleared.
diff --git a/lib/java/com/google/android/material/textfield/res-public/values/public.xml b/lib/java/com/google/android/material/textfield/res-public/values/public.xml
index 91b7579c299..9c707a8867e 100644
--- a/lib/java/com/google/android/material/textfield/res-public/values/public.xml
+++ b/lib/java/com/google/android/material/textfield/res-public/values/public.xml
@@ -53,6 +53,7 @@
+
diff --git a/lib/java/com/google/android/material/textfield/res/values/attrs.xml b/lib/java/com/google/android/material/textfield/res/values/attrs.xml
index be6ff8c5907..9c59726cc79 100644
--- a/lib/java/com/google/android/material/textfield/res/values/attrs.xml
+++ b/lib/java/com/google/android/material/textfield/res/values/attrs.xml
@@ -92,6 +92,8 @@
Should be set when the error message has special characters that a
screen reader is not able to announce properly. -->
+
+
diff --git a/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java b/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
index da9f0e8791b..dc3d7fa1fec 100644
--- a/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
+++ b/lib/javatests/com/google/android/material/datepicker/RangeDateSelectorTest.java
@@ -305,6 +305,55 @@ public void getSelectionContentDescription_startNotEmpty_endNotEmpty_returnsStar
assertThat(contentDescription).isEqualTo(expected);
}
+ @Test
+ public void getError_emptyDates_isNull() {
+ assertThat(rangeDateSelector.getError()).isNull();
+ }
+
+ @Test
+ public void getError_validStartDate_isNull() {
+ View root = getRootView();
+ TextInputLayout startTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_start);
+ activity.setContentView(root);
+ startTextInput.getEditText().setText("1/1/11");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(rangeDateSelector.getError()).isNull();
+ }
+
+ @Test
+ public void getError_validEndDate_isNull() {
+ View root = getRootView();
+ TextInputLayout endTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_end);
+ activity.setContentView(root);
+ endTextInput.getEditText().setText("1/1/11");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(rangeDateSelector.getError()).isNull();
+ }
+
+ @Test
+ public void getError_invalidStartDate_isNotEmpty() {
+ View root = getRootView();
+ TextInputLayout startTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_start);
+ activity.setContentView(root);
+ startTextInput.getEditText().setText("1/1/");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(rangeDateSelector.getError()).isNotEmpty();
+ }
+
+ @Test
+ public void getError_invalidEndDate_isNotEmpty() {
+ View root = getRootView();
+ TextInputLayout endTextInput = root.findViewById(R.id.mtrl_picker_text_input_range_end);
+ activity.setContentView(root);
+ endTextInput.getEditText().setText("1/1/");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(rangeDateSelector.getError()).isNotEmpty();
+ }
+
@Test
public void getSelectedRanges_fullRange() {
Calendar setToStart = UtcDates.getUtcCalendar();
diff --git a/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java b/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
index 464488c8fa5..935cd7b0092 100644
--- a/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
+++ b/lib/javatests/com/google/android/material/datepicker/SingleDateSelectorTest.java
@@ -37,6 +37,7 @@
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLooper;
@RunWith(RobolectricTestRunner.class)
public class SingleDateSelectorTest {
@@ -128,6 +129,33 @@ public void getSelectionContentDescription_notEmpty_returnsDate() {
assertThat(contentDescription).isEqualTo(expected);
}
+ @Test
+ public void getError_emptyDate_isNull() {
+ assertThat(singleDateSelector.getError()).isNull();
+ }
+
+ @Test
+ public void getError_validDate_isNull() {
+ View root = getRootView();
+ ((ViewGroup) activity.findViewById(android.R.id.content)).addView(root);
+ TextInputLayout textInputLayout = root.findViewById(R.id.mtrl_picker_text_input_date);
+ textInputLayout.getEditText().setText("1/1/11");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(singleDateSelector.getError()).isNull();
+ }
+
+ @Test
+ public void getError_invalidDate_isNotEmpty() {
+ View root = getRootView();
+ ((ViewGroup) activity.findViewById(android.R.id.content)).addView(root);
+ TextInputLayout textInputLayout = root.findViewById(R.id.mtrl_picker_text_input_date);
+ textInputLayout.getEditText().setText("1/1/");
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ assertThat(singleDateSelector.getError()).isNotEmpty();
+ }
+
@Test
public void getSelectedRanges_isEmpty() {
Calendar calendar = UtcDates.getUtcCalendar();
diff --git a/tests/javatests/com/google/android/material/testutils/TextInputLayoutActions.java b/tests/javatests/com/google/android/material/testutils/TextInputLayoutActions.java
index e98a3a4521c..17eed4cd56e 100644
--- a/tests/javatests/com/google/android/material/testutils/TextInputLayoutActions.java
+++ b/tests/javatests/com/google/android/material/testutils/TextInputLayoutActions.java
@@ -124,6 +124,26 @@ public void perform(UiController uiController, View view) {
};
}
+ public static ViewAction setErrorAccessibilityLiveRegion(final int accessibilityLiveRegion) {
+ return new ViewAction() {
+ @Override
+ public Matcher getConstraints() {
+ return isAssignableFrom(TextInputLayout.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Sets the error message's accessibility live region";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ TextInputLayout layout = (TextInputLayout) view;
+ layout.setErrorAccessibilityLiveRegion(accessibilityLiveRegion);
+ }
+ };
+ }
+
public static ViewAction setHelperTextEnabled(final boolean enabled) {
return new ViewAction() {
@Override
diff --git a/tests/javatests/com/google/android/material/textfield/TextInputLayoutTest.java b/tests/javatests/com/google/android/material/textfield/TextInputLayoutTest.java
index 970892032bf..a5161d8b363 100644
--- a/tests/javatests/com/google/android/material/textfield/TextInputLayoutTest.java
+++ b/tests/javatests/com/google/android/material/textfield/TextInputLayoutTest.java
@@ -40,6 +40,7 @@
import static com.google.android.material.testutils.TextInputLayoutActions.setCounterEnabled;
import static com.google.android.material.testutils.TextInputLayoutActions.setCounterMaxLength;
import static com.google.android.material.testutils.TextInputLayoutActions.setError;
+import static com.google.android.material.testutils.TextInputLayoutActions.setErrorAccessibilityLiveRegion;
import static com.google.android.material.testutils.TextInputLayoutActions.setErrorContentDescription;
import static com.google.android.material.testutils.TextInputLayoutActions.setErrorEnabled;
import static com.google.android.material.testutils.TextInputLayoutActions.setErrorTextAppearance;
@@ -68,7 +69,6 @@
import android.os.Build;
import android.os.Parcelable;
import android.text.TextPaint;
-import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
@@ -120,14 +120,6 @@ public TestTextInputLayout(Context context) {
super(context);
}
- public TestTextInputLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public TestTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
@Override
protected void animateToExpansionFraction(float target) {
super.animateToExpansionFraction(target);
@@ -499,6 +491,38 @@ public void testSetErrorContentDescription() {
assertEquals(errorContentDesc, textInputLayout.getErrorContentDescription().toString());
}
+ @Test
+ public void testSetErrorAccessibilityLiveRegion() {
+ int errorAccessibilityLiveRegion = ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE;
+ // Set error and error accessibility live region.
+ onView(withId(R.id.textinput))
+ .perform(setErrorEnabled(true))
+ .perform(setError(ERROR_MESSAGE_1))
+ .perform(setErrorAccessibilityLiveRegion(errorAccessibilityLiveRegion));
+
+ final Activity activity = activityTestRule.getActivity();
+ final TextInputLayout textInputLayout = activity.findViewById(R.id.textinput);
+
+ // Assert the error accessibility live region is as expected.
+ assertEquals(errorAccessibilityLiveRegion, textInputLayout.getErrorAccessibilityLiveRegion());
+ }
+
+ @Test
+ public void testDefaultErrorAccessibilityLiveRegionIsPolite() {
+ // Set error.
+ onView(withId(R.id.textinput))
+ .perform(setErrorEnabled(true))
+ .perform(setError(ERROR_MESSAGE_1));
+
+ final Activity activity = activityTestRule.getActivity();
+ final TextInputLayout textInputLayout = activity.findViewById(R.id.textinput);
+
+ // Assert the error accessibility live region is as expected.
+ assertEquals(
+ ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE,
+ textInputLayout.getErrorAccessibilityLiveRegion());
+ }
+
@Test
public void testSetTypefaceUpdatesErrorView() {
onView(withId(R.id.textinput))