Skip to content

Commit

Permalink
Feature - Add accessibility support for android date picker (#314)
Browse files Browse the repository at this point in the history
* Implemented accessibility support for android date picker.

* Created new function to return accessible text.
Changed access modifier of few functions to public.

* removed duplicate contentDescription for accessibility to work correct.

* Added finnish and swedish localizations for accessible items in datepicker.

* Added localisation english texts.

* Implemented localisations for accessible selections made inside android datepicker.

* Added getLocaleStringResource function for getting localised string resource.

* Added required imports for getLocaleStringResource function.

* Content description set based on localised strings from resources initially.

* Implemented meaningful content description for dateTime picker.
Added new keys for time prefix.
Content description set when screen reader is focused on picker wheels.

* Updated correct finnish and swedish translations.

* refactor: move accessibility to its own class

Co-authored-by: Jencir CJ <[email protected]>
Co-authored-by: Henning Hall <[email protected]>
  • Loading branch information
3 people authored May 4, 2021
1 parent 1c4063d commit 5038b6b
Show file tree
Hide file tree
Showing 12 changed files with 260 additions and 13 deletions.
30 changes: 30 additions & 0 deletions android/src/main/java/com/henninghall/date_picker/LocaleUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.henninghall.date_picker;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
Expand Down Expand Up @@ -61,4 +66,29 @@ public static boolean localeUsesAmPm(Locale locale){
return df instanceof SimpleDateFormat && ((SimpleDateFormat) df).toPattern().contains("a");
}

public static String getLocaleStringResource(Locale requestedLocale, int resourceId, Context context) {
String result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // use latest api
Configuration config = new Configuration(context.getResources().getConfiguration());
config.setLocale(requestedLocale);
result = context.createConfigurationContext(config).getText(resourceId).toString();
}
else { // support older android versions
Resources resources = context.getResources();
Configuration conf = resources.getConfiguration();
Locale savedLocale = conf.locale;
conf.locale = requestedLocale;
resources.updateConfiguration(conf, null);

// retrieve resources from desired locale
result = resources.getString(resourceId);

// restore original locale
conf.locale = savedLocale;
resources.updateConfiguration(conf, null);
}

return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public void update() {
uiManager.updateDisplayValues();
}

if (didUpdate(ModeProp.name, LocaleProp.name)) {
uiManager.updateAccessibilityValues();
}

uiManager.setWheelsToDate();

updatedProps = new ArrayList<>();
Expand Down
6 changes: 6 additions & 0 deletions android/src/main/java/com/henninghall/date_picker/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,10 @@ public static int getShortestScrollOption(int from, int to, final int maxValue,
if (from + option1 < 0) return option2;
return option1;
}

public static String getLocalisedStringFromResources(Locale locale, String tagName) {
int selectedKey = DatePickerManager.context.getResources().getIdentifier(tagName,"string",DatePickerManager.context.getPackageName());
String localisedText = LocaleUtils.getLocaleStringResource(locale, selectedKey, DatePickerManager.context);
return localisedText;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.henninghall.date_picker.ui;

import android.view.View;
import android.view.accessibility.AccessibilityEvent;

import com.henninghall.date_picker.State;
import com.henninghall.date_picker.Utils;
import com.henninghall.date_picker.wheelFunctions.WheelFunction;
import com.henninghall.date_picker.wheels.Wheel;

import java.util.Locale;

public class Accessibility {

public static class SetAccessibilityDelegate implements WheelFunction {

private final Locale locale;

public SetAccessibilityDelegate(Locale locale) {
this.locale = locale;
}

@Override
public void apply(Wheel wheel) {
final View view = wheel.picker.getView();
view.setAccessibilityDelegate(
new View.AccessibilityDelegate(){
@Override
public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(host, event);
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) {
String resourceKey = view.getTag().toString()+"_description";
String localeTag = Utils.getLocalisedStringFromResources(locale, resourceKey);
// Screen reader reads the content description when focused on each picker wheel
view.setContentDescription(localeTag);
}
}
}
);
}
}

private final State state;
private final Wheels wheels;

public Accessibility(State state, Wheels wheels){
this.state = state;
this.wheels = wheels;
}

public void update(Wheel picker){
String tagName = picker.picker.getView().getTag().toString();
String selectedDateString = getAccessibleTextForSelectedDate();
String descriptionPrefix = Utils.getLocalisedStringFromResources(state.getLocale(), "selected_"+tagName+"_description");
String descriptionPostFix = Utils.getLocalisedStringFromResources(state.getLocale(), "selected_value_description");

picker.picker.getView().setContentDescription(descriptionPrefix + ", "+ descriptionPostFix + " "+ selectedDateString);
}

private String getAccessibleTextForSelectedDate() {
String accessibleText;
switch(state.getMode()) {
case date:
accessibleText = wheels.getDateString();
break;
case time:
accessibleText = wheels.getTimeString();
break;
default:
// default is dateTime
String timePrefix = Utils.getLocalisedStringFromResources(state.getLocale(), "time_tag");
String hourPrefix = Utils.getLocalisedStringFromResources(state.getLocale(), "hour_tag");
String minutesPrefix = Utils.getLocalisedStringFromResources(state.getLocale(), "minutes_tag");
accessibleText = wheels.getAccessibleDateTimeString(timePrefix, hourPrefix, minutesPrefix);
break;
}
return accessibleText;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.henninghall.date_picker.wheelFunctions.AnimateToDate;
import com.henninghall.date_picker.wheelFunctions.Refresh;
import com.henninghall.date_picker.wheelFunctions.SetDate;
import com.henninghall.date_picker.wheelFunctions.SetDividerHeight;
import com.henninghall.date_picker.wheelFunctions.TextColor;
import com.henninghall.date_picker.wheelFunctions.UpdateVisibility;
import com.henninghall.date_picker.wheelFunctions.HorizontalPadding;
Expand All @@ -22,11 +21,13 @@ public class UIManager {
private Wheels wheels;
private FadingOverlay fadingOverlay;
private WheelScroller wheelScroller = new WheelScroller();
private Accessibility accessibility;

public UIManager(State state, View rootView){
this.state = state;
this.rootView = rootView;
wheels = new Wheels(state, rootView);
accessibility = new Accessibility(state, wheels);
addOnChangeListener();
}

Expand Down Expand Up @@ -90,4 +91,12 @@ public void updateDividerHeight() {
public void updateWheelPadding() {
wheels.applyOnVisible(new HorizontalPadding());
}

public void updateContentDescription(Wheel picker) {
accessibility.update(picker);
}

public void updateAccessibilityValues() {
wheels.applyOnAll(new Accessibility.SetAccessibilityDelegate(state.getLocale()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public void onChange(Wheel picker) {
return;
}

uiManager.updateContentDescription(picker);

emitDateChangeEvent(selectedDate);
}

Expand Down
17 changes: 15 additions & 2 deletions android/src/main/java/com/henninghall/date_picker/ui/Wheels.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public class Wheels {
changeAmPmWhenPassingMidnightOrNoon();
}

private Picker getPickerWithId(int id){
private Picker getPickerWithId(final int id){
return (Picker) rootView.findViewById(id);
}

Expand Down Expand Up @@ -136,7 +136,7 @@ private String getDateString(int daysToSubtract){
return dayWheel.getValue();
}

private String getTimeString(){
String getTimeString(){
return hourWheel.getValue()
+ " " + minutesWheel.getValue()
+ ampmWheel.getValue();
Expand All @@ -146,6 +146,19 @@ String getDateTimeString() {
return getDateTimeString(0);
}

String getDateString() {
return getDateString(0);
}

String getAccessibleDateTimeString(String timePrefix, String hourTag, String minutesTag) {
String date = getDateString();
String hour = hourWheel.getValue();
String minutes = minutesWheel.getValue();
String ampm = ampmWheel.getValue();
String time = timePrefix+ " "+ hour + hourTag + minutes + minutesTag + ampm;
return date+", "+ time;
}

String getDisplayValue() {
StringBuilder sb = new StringBuilder();
for (Wheel wheel: getOrderedVisibleWheels()) {
Expand Down
24 changes: 22 additions & 2 deletions android/src/main/res/layout/ios_clone.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
custom:npv_TextSizeSelected="21dp"
custom:npv_TextColorSelected="#000000"
custom:npv_TextColorNormal="#aaaaaa"
custom:npv_DividerColor="#cccccc" />
custom:npv_DividerColor="#cccccc"
android:contentDescription="@string/year_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.IosClone
android:id="@+id/month"
Expand All @@ -49,6 +52,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextColorNormal="#aaaaaa"
custom:npv_DividerColor="#cccccc"
android:contentDescription="@string/month_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>

<com.henninghall.date_picker.pickers.IosClone
Expand All @@ -64,6 +70,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextColorNormal="#aaaaaa"
custom:npv_DividerColor="#cccccc"
android:contentDescription="@string/date_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<com.henninghall.date_picker.pickers.IosClone
android:id="@+id/day"
Expand All @@ -77,6 +86,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextColorNormal="#aaaaaa"
custom:npv_DividerColor="#cccccc"
android:contentDescription="@string/day_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<com.henninghall.date_picker.pickers.IosClone
android:id="@+id/hour"
Expand All @@ -91,6 +103,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextSizeNormal="18dp"
custom:npv_TextSizeSelected="21dp"
android:contentDescription="@string/hour_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<com.henninghall.date_picker.pickers.IosClone
android:id="@+id/minutes"
Expand All @@ -105,6 +120,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextSizeNormal="18dp"
custom:npv_TextSizeSelected="21dp"
android:contentDescription="@string/minutes_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>

<com.henninghall.date_picker.pickers.IosClone
Expand All @@ -120,6 +138,9 @@
custom:npv_TextColorSelected="#000000"
custom:npv_TextSizeNormal="18dp"
custom:npv_TextSizeSelected="21dp"
android:contentDescription="@string/ampm_description"
android:focusable="true"
android:focusableInTouchMode="true"
/>

<com.henninghall.date_picker.pickers.IosClone
Expand Down Expand Up @@ -158,7 +179,6 @@
android:id="@+id/overlay_bottom"
android:src="@drawable/overlay"
android:layout_width="match_parent"
android:contentDescription="@string/overlay"
android:layout_height="0dp"
android:layout_weight=".15"
/>
Expand Down
35 changes: 28 additions & 7 deletions android/src/main/res/layout/native_picker.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,64 @@
android:id="@+id/year"
android:theme="@style/android_native_theme"
style="@style/android_native"
android:tag="year" />
android:tag="year"
android:contentDescription="@string/year_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/month"
android:theme="@style/android_native_theme"
style="@style/android_native"
android:tag="month" />
android:tag="month"
android:contentDescription="@string/month_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/date"
android:theme="@style/android_native_theme"
style="@style/android_native_small"
android:tag="date" />
android:tag="date"
android:contentDescription="@string/date_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/day"
android:theme="@style/android_native_theme"
style="@style/android_native"
android:tag="day" />
android:tag="day"
android:contentDescription="@string/day_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/hour"
android:theme="@style/android_native_theme"
style="@style/android_native_small"
android:tag="hour" />
android:tag="hour"
android:contentDescription="@string/hour_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/minutes"
android:theme="@style/android_native_theme"
style="@style/android_native_small"
android:tag="minutes" />
android:tag="minutes"
android:contentDescription="@string/minutes_description"
android:focusable="true"
android:focusableInTouchMode="true" />

<com.henninghall.date_picker.pickers.AndroidNative
android:id="@+id/ampm"
android:theme="@style/android_native_theme"
style="@style/android_native"
android:tag="ampm" />
android:tag="ampm"
android:contentDescription="@string/ampm_description"
android:focusable="true"
android:focusableInTouchMode="true" />
</LinearLayout>

</LinearLayout>
Loading

0 comments on commit 5038b6b

Please sign in to comment.