Skip to content

Commit

Permalink
[MaterialDatePicker] Add method to specify a content description to d…
Browse files Browse the repository at this point in the history
…ecorated views

PiperOrigin-RevId: 474622363
  • Loading branch information
paulfthomas authored and afohrman committed Sep 16, 2022
1 parent 09f1ee5 commit 7bc5689
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 11 deletions.
Expand Up @@ -57,6 +57,24 @@ public ColorStateList getBackgroundColor(
: null;
}

@Nullable
@Override
public CharSequence getContentDescription(
@NonNull Context context,
int year,
int month,
int day,
boolean valid,
boolean selected,
@Nullable CharSequence originalContentDescription) {
if (!valid || !shouldShowHighlight(year, month, day)) {
return originalContentDescription;
}
return String.format(
context.getString(R.string.cat_picker_day_view_decorator_highlights_content_description),
originalContentDescription);
}

private boolean shouldShowHighlight(int year, int month, int day) {
for (Calendar calendar : highlightDays) {
if (calendar.get(Calendar.YEAR) == year
Expand Down
Expand Up @@ -28,6 +28,7 @@
import android.os.Parcelable;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.datepicker.DayViewDecorator;
import java.util.ArrayList;
Expand Down Expand Up @@ -61,6 +62,24 @@ public Drawable getCompoundDrawableBottom(
return selectIndicatorDrawable(year, month, day, valid, selected);
}

@Nullable
@Override
public CharSequence getContentDescription(
@NonNull Context context,
int year,
int month,
int day,
boolean valid,
boolean selected,
@Nullable CharSequence originalContentDescription) {
if (!valid || !shouldShowIndicator(year, month, day)) {
return originalContentDescription;
}
return String.format(
context.getString(R.string.cat_picker_day_view_decorator_dots_content_description),
originalContentDescription);
}

private Drawable selectIndicatorDrawable(
int year, int month, int day, boolean valid, boolean selected) {
if (!valid || !shouldShowIndicator(year, month, day)) {
Expand Down
Expand Up @@ -118,7 +118,7 @@
<string name="cat_picker_day_view_decorator_none">None</string>
<!-- Indicates that the picker will use a dots day view decorator [CHAR LIMIT=50] -->
<string name="cat_picker_day_view_decorator_dots">Dots</string>
<!-- Indicates that the picker will use a dots day view decorator [CHAR LIMIT=50] -->
<!-- Indicates that the picker will use a highlights day view decorator [CHAR LIMIT=50] -->
<string name="cat_picker_day_view_decorator_highlights">Highlights</string>

<!-- Grouping label for a set of radio buttons that choose the positive button [CHAR LIMIT=50] -->
Expand All @@ -139,4 +139,9 @@
<string name="cat_picker_positive_button_text">DONE</string>
<!-- The custom negative button text -->
<string name="cat_picker_negative_button_text">Negative</string>

<!-- The custom dots day view decorator's content description [CHAR LIMIT=120] -->
<string name="cat_picker_day_view_decorator_dots_content_description">%s Dotted</string>
<!-- The custom highlights day view decorator's content description [CHAR LIMIT=120] -->
<string name="cat_picker_day_view_decorator_highlights_content_description">%s Highlighted</string>
</resources>
26 changes: 21 additions & 5 deletions lib/java/com/google/android/material/datepicker/DateStrings.java
Expand Up @@ -100,6 +100,19 @@ static String getYearMonthDayOfWeekDay(long timeInMillis, Locale locale) {
return UtcDates.getFullFormat(locale).format(new Date(timeInMillis));
}

/**
* Does not show year if date is within current year.
*
* @param timeInMillis milliseconds since UTC epoch.
* @return Formatted date string.
*/
static String getOptionalYearMonthDayOfWeekDay(long timeInMillis) {
if (isDateWithinCurrentYear(timeInMillis)) {
return getMonthDayOfWeekDay(timeInMillis);
}
return getYearMonthDayOfWeekDay(timeInMillis);
}

static String getDateString(long timeInMillis) {
return getDateString(timeInMillis, null);
}
Expand All @@ -116,19 +129,22 @@ static String getDateString(long timeInMillis) {
* @return Formatted date string.
*/
static String getDateString(long timeInMillis, @Nullable SimpleDateFormat userDefinedDateFormat) {
Calendar currentCalendar = UtcDates.getTodayCalendar();
Calendar calendarDate = UtcDates.getUtcCalendar();
calendarDate.setTimeInMillis(timeInMillis);

if (userDefinedDateFormat != null) {
Date date = new Date(timeInMillis);
return userDefinedDateFormat.format(date);
} else if (currentCalendar.get(Calendar.YEAR) == calendarDate.get(Calendar.YEAR)) {
} else if (isDateWithinCurrentYear(timeInMillis)) {
return getMonthDay(timeInMillis);
}
return getYearMonthDay(timeInMillis);
}

private static boolean isDateWithinCurrentYear(long timeInMillis) {
Calendar currentCalendar = UtcDates.getTodayCalendar();
Calendar calendarDate = UtcDates.getUtcCalendar();
calendarDate.setTimeInMillis(timeInMillis);
return currentCalendar.get(Calendar.YEAR) == calendarDate.get(Calendar.YEAR);
}

static Pair<String, String> getDateRangeString(@Nullable Long start, @Nullable Long end) {
return getDateRangeString(start, end, null);
}
Expand Down
Expand Up @@ -146,4 +146,32 @@ public ColorStateList getBackgroundColor(
@NonNull Context context, int year, int month, int day, boolean valid, boolean selected) {
return null;
}

/**
* Override this method to return the day view's content description.
*
* @param context The context of the day view
* @param year The year number corresponding to the day view (see {@link java.util.Calendar.YEAR})
* @param month The month number (0-11) corresponding to the day view (see {@link
* java.util.Calendar.MONTH})
* @param day The day of month number corresponding to the day view (see {@link
* java.util.Calendar.DAY_OF_MONTH})
* @param valid Boolean for whether the day view is in a valid state (if not valid, the day view
* will likely look and behave disabled)
* @param selected Boolean for whether the day view is in a selected state (if selected, the day
* view will likely have a filled color background)
* @param originalContentDescription The original day view's content description
* @return The content description
*/
@Nullable
public CharSequence getContentDescription(
@NonNull Context context,
int year,
int month,
int day,
boolean valid,
boolean selected,
@Nullable CharSequence originalContentDescription) {
return originalContentDescription;
}
}
18 changes: 13 additions & 5 deletions lib/java/com/google/android/material/datepicker/MonthAdapter.java
Expand Up @@ -138,11 +138,7 @@ public TextView getView(int position, @Nullable View convertView, @NonNull ViewG
Locale locale = dayTextView.getResources().getConfiguration().locale;
dayTextView.setText(String.format(locale, "%d", dayNumber));
long dayInMillis = month.getDay(dayNumber);
if (month.year == Month.current().year) {
dayTextView.setContentDescription(DateStrings.getMonthDayOfWeekDay(dayInMillis));
} else {
dayTextView.setContentDescription(DateStrings.getYearMonthDayOfWeekDay(dayInMillis));
}
dayTextView.setContentDescription(DateStrings.getOptionalYearMonthDayOfWeekDay(dayInMillis));
dayTextView.setVisibility(View.VISIBLE);
dayTextView.setEnabled(true);
}
Expand Down Expand Up @@ -208,6 +204,7 @@ private void updateSelectedState(@Nullable TextView dayTextView, long date, int

if (dayViewDecorator != null && dayNumber != NO_DAY_NUMBER) {
Context context = dayTextView.getContext();
long dayInMillis = month.getDay(dayNumber);
int year = month.year;
int month = this.month.month;

Expand All @@ -227,6 +224,17 @@ private void updateSelectedState(@Nullable TextView dayTextView, long date, int
dayViewDecorator.getCompoundDrawableBottom(
context, year, month, dayNumber, valid, selected);
dayTextView.setCompoundDrawables(drawableLeft, drawableTop, drawableRight, drawableBottom);

CharSequence decoratorContentDescription =
dayViewDecorator.getContentDescription(
context,
year,
month,
dayNumber,
valid,
selected,
DateStrings.getOptionalYearMonthDayOfWeekDay(dayInMillis));
dayTextView.setContentDescription(decoratorContentDescription);
} else {
style.styleItem(dayTextView);
}
Expand Down
Expand Up @@ -21,7 +21,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import java.util.Arrays;
import java.util.Calendar;
Expand Down Expand Up @@ -261,6 +266,71 @@ public void ilDaysOfPositions() {
assertDaysOfPositions(localizedDaysOfPositionsInFebruary2019);
}

@Test
public void dayViewDecorator_withIndicator_hasUpdatedContentDescription() {
DayViewDecorator decorator = getDecoratedMonthAdapter().dayViewDecorator;

CharSequence decoratorContentDescription =
decorator.getContentDescription(
ApplicationProvider.getApplicationContext(),
2018,
Calendar.JANUARY,
17,
true,
false,
"Original content description");
assertTrue("Original content description Test".contentEquals(decoratorContentDescription));
}

@Test
public void dayViewDecorator_withoutIndicator_hasOriginalContentDescription() {
DayViewDecorator decorator = getDecoratedMonthAdapter().dayViewDecorator;

CharSequence decoratorContentDescription =
decorator.getContentDescription(
ApplicationProvider.getApplicationContext(),
2018,
Calendar.JANUARY,
16,
true,
false,
"Original content description");
assertTrue("Original content description".contentEquals(decoratorContentDescription));
}

private MonthAdapter getDecoratedMonthAdapter() {
return new MonthAdapter(
Month.create(2018, Calendar.JANUARY),
new SingleDateSelector(),
new CalendarConstraints.Builder().build(),
new DayViewDecorator() {
@Nullable
@Override
public CharSequence getContentDescription(
@NonNull Context context,
int year,
int month,
int day,
boolean valid,
boolean selected,
@Nullable CharSequence originalContentDescription) {
if (year == 2018 && month == Calendar.JANUARY && day == 17) {
return originalContentDescription + " Test";
}
return super.getContentDescription(
context, year, month, day, valid, selected, originalContentDescription);
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {}
});
}

private void assertDaysOfPositions(Map<Integer, Integer> localizedDaysOfPositionsInFebruary2019) {
for (int day : localizedDaysOfPositionsInFebruary2019.keySet()) {
Calendar testCalendar = UtcDates.getUtcCalendar();
Expand Down

0 comments on commit 7bc5689

Please sign in to comment.