Skip to content

Added Accessibility Support for Screenreaders #112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.highlight.BarHighlighter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.renderer.BarChartRenderer;

import java.util.Locale;

/**
* Chart that draws bars.
*
Expand Down Expand Up @@ -275,4 +278,33 @@ public void setRoundedBarRadius(float mRoundedBarRadius) {
this.mDrawRoundedBars = true;
init();
}

@Override
public String getAccessibilityDescription() {

BarData barData = getBarData();

int entryCount = barData.getEntryCount();

// Find the min and max index
IAxisValueFormatter yAxisValueFormatter = getAxisLeft().getValueFormatter();
String minVal = yAxisValueFormatter.getFormattedValue(barData.getYMin(), null);
String maxVal = yAxisValueFormatter.getFormattedValue(barData.getYMax(), null);

// Data range...
IAxisValueFormatter xAxisValueFormatter = getXAxis().getValueFormatter();
String minRange = xAxisValueFormatter.getFormattedValue(barData.getXMin(), null);
String maxRange = xAxisValueFormatter.getFormattedValue(barData.getXMax(), null);

String entries = entryCount == 1 ? "entry" : "entries";

// Format the values of min and max; to recite them back

String description = String.format(Locale.getDefault(), "The bar chart has %d %s. " +
"The minimum value is %s and maximum value is %s." +
"Data ranges from %s to %s.",
entryCount, entries, minVal, maxVal, minRange, maxRange);

return description;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ protected void init() {
public BubbleData getBubbleData() {
return mData;
}

@Override
public String getAccessibilityDescription() {
return "This is bubble chart";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ protected void init() {
public CandleData getCandleData() {
return mData;
}

@Override
public String getAccessibilityDescription() {
return "This is a candlestick chart";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;

import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.animation.Easing.EasingFunction;
Expand Down Expand Up @@ -170,6 +171,16 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
private float mExtraTopOffset = 0.f, mExtraRightOffset = 0.f, mExtraBottomOffset = 0.f, mExtraLeftOffset = 0.f;

/**
* Tag for logging accessibility related content
*/
private String TAG = "abilityTag";

/**
* Additional data on top of dynamically generated description. This can be set by the user.
*/
private String accessibilitySummaryDescription = "";

/**
* default constructor for initialization in code
*/
public Chart(Context context) {
Expand Down Expand Up @@ -229,9 +240,11 @@ public void onAnimationUpdate(ValueAnimator animation) {
mInfoPaint.setTextSize(Utils.convertDpToPixel(12f));

if (mLogEnabled) {
Log.i("", "Chart.init()");
}
}
Log.i("", "Chart.init()");

// enable being detected by ScreenReader
setFocusable(true);
}}

// public void initWithDummyData() {
// ColorTemplate template = new ColorTemplate();
Expand Down Expand Up @@ -1649,4 +1662,38 @@ private void unbindDrawables(View view) {
public void setUnbindEnabled(boolean enabled) {
this.mUnbind = enabled;
}

// region accessibility

/**
*
* @return accessibility description must be created for each chart
*/
public abstract String getAccessibilityDescription();

public String getAccessibilitySummaryDescription() {
return accessibilitySummaryDescription;
}

public void setAccessibilitySummaryDescription(String accessibilitySummaryDescription) {
this.accessibilitySummaryDescription = accessibilitySummaryDescription;
}

@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {

boolean completed = super.dispatchPopulateAccessibilityEvent(event);
Log.d(TAG, "Dispatch called for Chart <View> and completed as " + completed);

event.getText().add(getAccessibilityDescription());

// Add the user generated summary after the dynamic summary is complete.
if (!TextUtils.isEmpty(this.getAccessibilitySummaryDescription())) {
event.getText().add(this.getAccessibilitySummaryDescription());
}

return true;
}

// endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,8 @@ protected void drawMarkers(Canvas canvas) {
}
}

@Override
public String getAccessibilityDescription() {
return "This is a combined chart";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import android.util.AttributeSet;

import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.renderer.LineChartRenderer;

import java.util.Locale;

/**
* Chart that draws lines, surfaces, circles, ...
*
Expand Down Expand Up @@ -47,4 +50,28 @@ protected void onDetachedFromWindow() {
}
super.onDetachedFromWindow();
}

@Override
public String getAccessibilityDescription() {
LineData lineData = getLineData();

int numberOfPoints = lineData.getEntryCount();

// Min and max values...
IAxisValueFormatter yAxisValueFormmater = getAxisLeft().getValueFormatter();
String minVal = yAxisValueFormmater.getFormattedValue(lineData.getYMin(), null);
String maxVal = yAxisValueFormmater.getFormattedValue(lineData.getYMax(), null);

// Data range...
IAxisValueFormatter xAxisValueFormatter = getXAxis().getValueFormatter();
String minRange = xAxisValueFormatter.getFormattedValue(lineData.getXMin(), null);
String maxRange = xAxisValueFormatter.getFormattedValue(lineData.getXMax(), null);

String entries = numberOfPoints == 1 ? "entry" : "entries";

return String.format(Locale.getDefault(), "The line chart has %d %s. " +
"The minimum value is %s and maximum value is %s." +
"Data ranges from %s to %s.",
numberOfPoints, entries, minVal, maxVal, minRange, maxRange);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;

import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieEntry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.highlight.PieHighlighter;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.renderer.PieChartRenderer;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
* View that represents a pie chart. Draws cake like slices.
Expand Down Expand Up @@ -801,4 +805,27 @@ protected void onDetachedFromWindow() {
}
super.onDetachedFromWindow();
}

@Override
public String getAccessibilityDescription() {

PieData pieData = getData();

int entryCount = pieData.getEntryCount();

StringBuilder builder = new StringBuilder();

builder.append(String.format(Locale.getDefault(), "The pie chart has %d entries.",
entryCount));

for (int i = 0; i < entryCount; i++) {
PieEntry entry = pieData.getDataSet().getEntryForIndex(i);
float percentage = (entry.getValue() / pieData.getYValueSum()) * 100;
builder.append(String.format(Locale.getDefault(), "%s has %.2f percent pie taken",
(TextUtils.isEmpty(entry.getLabel()) ? "No Label" : entry.getLabel()),
percentage));
}

return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,9 @@ public float getYChartMin() {
public float getYRange() {
return mYAxis.mAxisRange;
}

@Override
public String getAccessibilityDescription() {
return "This is a Radar chart";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,9 @@ public static ScatterShape[] getAllDefaultShapes() {
return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN};
}
}

@Override
public String getAccessibilityDescription() {
return "This is scatter chart";
}
}