From b2d00d076d94773b929e8125f4828f2f31df7ed0 Mon Sep 17 00:00:00 2001 From: Oliver Zimmerman <oezimmerman@gmail.com> Date: Fri, 3 Jul 2020 11:45:09 +0100 Subject: [PATCH] Added contentDescription containing 'button' via formatters for screen readers (EXPOSUREAPP-1542) (#794) * Update TracingViewModel.kt * Update TracingViewModel.kt * Added the word 'Button' to various contentDescriptions via formatters Added the word 'Button' to various contentDescriptions via formatters specifically for content without focusable subcontent that are explored by touch * Update FormatterSettingsHelper.kt * removed space in strings file and included in formatter instead * Added formatter tests Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> --- .../util/formatter/FormatterRiskHelper.kt | 28 ++++++ .../util/formatter/FormatterSettingsHelper.kt | 28 ++++++ .../src/main/res/layout/fragment_main.xml | 1 + .../src/main/res/layout/include_risk_card.xml | 1 + .../src/main/res/values-de/strings.xml | 2 + .../src/main/res/values-en/strings.xml | 2 + .../src/main/res/values/strings.xml | 2 + .../util/formatter/FormatterRiskHelperTest.kt | 80 +++++++++++++++++ .../formatter/FormatterSettingsHelperTest.kt | 90 +++++++++++++++++++ 9 files changed, 234 insertions(+) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt index 36bdc637d..ee5848aa2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelper.kt @@ -281,6 +281,34 @@ fun formatNextUpdate( } } +/** + * Formats the risk card content description of time when diagnosis keys will be updated + * from server again when applicable but appends the word button at the end for screen reader accessibility reasons + * + * @param riskLevelScore + * @param isBackgroundJobEnabled + * @return + */ +fun formatNextUpdateContentDescription( + riskLevelScore: Int?, + isBackgroundJobEnabled: Boolean? +): String { + val appContext = CoronaWarnApplication.getAppContext() + return if (isBackgroundJobEnabled != true) { + "" + } else { + return when (riskLevelScore) { + RiskLevelConstants.UNKNOWN_RISK_INITIAL, + RiskLevelConstants.LOW_LEVEL_RISK, + RiskLevelConstants.INCREASED_RISK -> appContext.getString( + R.string.risk_card_body_next_update + ) + " " + appContext.getString( + R.string.accessibility_button) + else -> "" + } + } +} + /** * Formats the risk details text display for each risk level * diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt index d1decae43..2bb50a259 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt @@ -119,6 +119,34 @@ fun formatTracingDescription(tracing: Boolean, bluetooth: Boolean, connection: B } } +/** + * Format the settings tracing content description text display depending on tracing status + * but appends the word button at the end for screen reader accessibility reasons + * + * @param tracing + * @param bluetooth + * @param connection + * @return String + */ +fun formatTracingContentDescription(tracing: Boolean, bluetooth: Boolean, connection: Boolean): String { + val appContext = CoronaWarnApplication.getAppContext() + return when (tracingStatusHelper(tracing, bluetooth, connection)) { + TracingStatusHelper.CONNECTION -> + appContext.getString(R.string.settings_tracing_body_connection_inactive) + + " " + appContext.getString(R.string.accessibility_button) + TracingStatusHelper.BLUETOOTH -> + appContext.getString(R.string.settings_tracing_body_bluetooth_inactive) + + " " + appContext.getString(R.string.accessibility_button) + TracingStatusHelper.TRACING_ACTIVE -> + appContext.getString(R.string.settings_tracing_body_active) + + " " + appContext.getString(R.string.accessibility_button) + TracingStatusHelper.TRACING_INACTIVE -> + appContext.getString(R.string.settings_tracing_body_inactive) + + " " + appContext.getString(R.string.accessibility_button) + else -> "" + } +} + /** * Formats the tracing body depending on the tracing status and the days since last exposure. * diff --git a/Corona-Warn-App/src/main/res/layout/fragment_main.xml b/Corona-Warn-App/src/main/res/layout/fragment_main.xml index 3dc5df211..db25a3b62 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_main.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_main.xml @@ -102,6 +102,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:focusable="false" + android:contentDescription="@{FormatterSettingsHelper.formatTracingContentDescription(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled())}" android:text="@{FormatterSettingsHelper.formatTracingDescription(tracingViewModel.isTracingEnabled(), settingsViewModel.isBluetoothEnabled(), settingsViewModel.isConnectionEnabled())}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/include_risk_card.xml b/Corona-Warn-App/src/main/res/layout/include_risk_card.xml index 9c50cbc70..48b1e91d6 100644 --- a/Corona-Warn-App/src/main/res/layout/include_risk_card.xml +++ b/Corona-Warn-App/src/main/res/layout/include_risk_card.xml @@ -227,6 +227,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_small" + android:contentDescription="@{FormatterRiskHelper.formatNextUpdateContentDescription(tracingViewModel.riskLevel, settingsViewModel.isBackgroundJobEnabled())}" android:text="@{FormatterRiskHelper.formatNextUpdate(tracingViewModel.riskLevel, settingsViewModel.isBackgroundJobEnabled())}" android:textColor="@{FormatterRiskHelper.formatStableTextColor(tracingViewModel.riskLevel)}" app:layout_constraintEnd_toEndOf="parent" diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 6d7e75c43..e9dca21e1 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -84,6 +84,8 @@ <string name="accessibility_close">"Schließen"</string> <!-- XACT: menu description for screen readers --> <string name="accessibility_logo">"Corona-Warn-App"</string> + <!-- XACT: button description for screen readers to be appended at the end of content without focusable subcontent that are explored by touch --> + <string name="accessibility_button">"Button"</string> <!-- #################################### Menu diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index 56f08e8cc..4ebc2ce02 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -84,6 +84,8 @@ <string name="accessibility_close">"Close"</string> <!-- XACT: menu description for screen readers --> <string name="accessibility_logo">"Corona-Warn-App"</string> + <!-- XACT: button description for screen readers to be appended at the end of content without focusable subcontent that are explored by touch --> + <string name="accessibility_button">"Button"</string> <!-- #################################### Menu diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index 54beed16e..78b823898 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -84,6 +84,8 @@ <string name="accessibility_close">"Close"</string> <!-- XACT: menu description for screen readers --> <string name="accessibility_logo">"Corona-Warn-App"</string> + <!-- XACT: button description for screen readers to be appended at the end of content without focusable subcontent that are explored by touch --> + <string name="accessibility_button">"Button"</string> <!-- #################################### Menu diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelperTest.kt index 3d595008e..d1830e3b4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterRiskHelperTest.kt @@ -168,6 +168,21 @@ class FormatterRiskHelperTest { ) } + private fun formatNextUpdateContentDescriptionBase( + iRiskLevelScore: Int?, + bIsBackgroundJobEnabled: Boolean?, + sValue: String + ) { + every { context.getString(R.string.risk_card_body_next_update) } returns R.string.risk_card_body_next_update.toString() + every { context.getString(R.string.accessibility_button) } returns R.string.accessibility_button.toString() + + val result = + formatNextUpdateContentDescription(riskLevelScore = iRiskLevelScore, isBackgroundJobEnabled = bIsBackgroundJobEnabled) + assertThat( + result, `is`(sValue) + ) + } + private fun formatRiskDetailsRiskLevelBodyBase( iRiskLevelScore: Int?, iDaysSinceLastExposure: Int?, @@ -955,6 +970,71 @@ class FormatterRiskHelperTest { ) } + @Test + fun formatNextUpdateContentDescription() { + formatNextUpdateContentDescriptionBase(iRiskLevelScore = null, bIsBackgroundJobEnabled = null, sValue = "") + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.INCREASED_RISK, + bIsBackgroundJobEnabled = null, + sValue = "" + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, + bIsBackgroundJobEnabled = null, + sValue = "" + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.NO_CALCULATION_POSSIBLE_TRACING_OFF, + bIsBackgroundJobEnabled = null, + sValue = "" + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, + bIsBackgroundJobEnabled = null, + sValue = "" + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, + bIsBackgroundJobEnabled = null, + sValue = "" + ) + + formatNextUpdateContentDescriptionBase(iRiskLevelScore = null, bIsBackgroundJobEnabled = true, sValue = "") + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.INCREASED_RISK, + bIsBackgroundJobEnabled = true, + sValue = context.getString( + R.string.risk_card_body_next_update + ) + " " + context.getString( + R.string.accessibility_button + ) + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, + bIsBackgroundJobEnabled = true, + sValue = "" + ) + + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, + bIsBackgroundJobEnabled = true, + sValue = context.getString( + R.string.risk_card_body_next_update + ) + " " + context.getString( + R.string.accessibility_button + ) + ) + formatNextUpdateContentDescriptionBase( + iRiskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, + bIsBackgroundJobEnabled = true, + sValue = context.getString( + R.string.risk_card_body_next_update + ) + " " + context.getString( + R.string.accessibility_button + ) + ) + } + @Test fun formatRiskDetailsRiskLevelBody() { formatRiskDetailsRiskLevelBodyBase(iRiskLevelScore = null, iDaysSinceLastExposure = 0, sValue = "") diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt index cb6f079b1..79b94264e 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt @@ -105,6 +105,29 @@ class FormatterSettingsHelperTest { assertThat(result, `is`((context.getString(iValue)))) } + + private fun formatTracingContentDescriptionBase( + bTracing: Boolean, + bBluetooth: Boolean, + bConnection: Boolean, + sValue: String + ) { + every { context.getString(R.string.settings_tracing_body_bluetooth_inactive) } returns R.string.settings_tracing_body_bluetooth_inactive.toString() + every { context.getString(R.string.settings_tracing_body_connection_inactive) } returns R.string.settings_tracing_body_connection_inactive.toString() + every { context.getString(R.string.settings_tracing_body_active) } returns R.string.settings_tracing_body_active.toString() + every { context.getString(R.string.settings_tracing_body_inactive) } returns R.string.settings_tracing_body_inactive.toString() + every { context.getString(R.string.accessibility_button) } returns R.string.accessibility_button.toString() + + val result = formatTracingContentDescription( + tracing = bTracing, + bluetooth = bBluetooth, + connection = bConnection + ) + assertThat( + result, `is`(sValue) + ) + } + private fun formatNotificationsTitleBase(bValue: Boolean) { val result = formatNotificationsTitle(notifications = bValue) assertThat( @@ -492,6 +515,73 @@ class FormatterSettingsHelperTest { ) } + @Test + fun formatTracingContentDescription() { + // When tracing is true, bluetooth is true, connection is true + formatTracingContentDescriptionBase( + bTracing = true, + bBluetooth = true, + bConnection = true, + sValue = R.string.settings_tracing_body_active.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is false, bluetooth is false, connection is false + formatTracingContentDescriptionBase( + bTracing = false, + bBluetooth = false, + bConnection = false, + sValue = R.string.settings_tracing_body_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is true, bluetooth is false, connection is false + formatTracingContentDescriptionBase( + bTracing = true, + bBluetooth = false, + bConnection = false, + sValue = R.string.settings_tracing_body_bluetooth_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is true, bluetooth is true, connection is false + formatTracingContentDescriptionBase( + bTracing = true, + bBluetooth = true, + bConnection = false, + sValue = R.string.settings_tracing_body_connection_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is false, bluetooth is true, connection is false + formatTracingContentDescriptionBase( + bTracing = false, + bBluetooth = true, + bConnection = false, + sValue = R.string.settings_tracing_body_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is false, bluetooth is true, connection is true + formatTracingContentDescriptionBase( + bTracing = false, + bBluetooth = true, + bConnection = true, + sValue = R.string.settings_tracing_body_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is true, bluetooth is false, connection is true + formatTracingContentDescriptionBase( + bTracing = true, + bBluetooth = false, + bConnection = true, + sValue = R.string.settings_tracing_body_bluetooth_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + + // When tracing is false, bluetooth is false, connection is true + formatTracingContentDescriptionBase( + bTracing = false, + bBluetooth = false, + bConnection = true, + sValue = R.string.settings_tracing_body_inactive.toString() + " " +R.string.accessibility_button.toString() + ) + } + @Test fun formatNotificationsTitle() { // When status true -- GitLab