Skip to content

Commit

Permalink
Fix Android AlertFragment Title Accessibility (#45395)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #45395

## Changelog:

[Android] [Fixed] - Fix AlertDialog title accessibility
Pull Request resolved: #45048

Making title accessible for android AlertFragment

Reviewed By: blavalla, susnchen

Differential Revision: D58684576

fbshipit-source-id: 83c8cfe9e7aacdf587d325d957694c3c7daa360c
  • Loading branch information
Abbondanzo authored and facebook-github-bot committed Jul 12, 2024
1 parent bac5f1f commit 80a3ed7
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/react-native/ReactAndroid/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ android {
listOf(
"src/main/res/devsupport",
"src/main/res/shell",
"src/main/res/views/alert",
"src/main/res/views/modal",
"src/main/res/views/uimanager"))
java.exclude("com/facebook/annotationprocessors")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.fragment.app.DialogFragment;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.R;

/** A fragment used to display the dialog. */
@Nullsafe(Nullsafe.Mode.LOCAL)
Expand Down Expand Up @@ -66,15 +75,55 @@ private static boolean isAppCompatTheme(Context activityContext) {
return isAppCompat;
}

/**
* Creates a custom dialog title View that has the role of "Heading" and focusable for
* accessibility purposes.
*
* @returns accessible TextView title
*/
private static View getAccessibleTitle(Context activityContext, String titleText) {
LayoutInflater inflater = LayoutInflater.from(activityContext);

// This layout matches the sizing and styling of AlertDialog's title_template (minus the icon)
// since the whole thing gets tossed out when setting a custom title
View titleContainer = inflater.inflate(R.layout.alert_title_layout, null);

TextView accessibleTitle =
Assertions.assertNotNull(titleContainer.findViewById(R.id.alert_title));
accessibleTitle.setText(titleText);
accessibleTitle.setFocusable(true);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
accessibleTitle.setAccessibilityHeading(true);
} else {
ViewCompat.setAccessibilityDelegate(
accessibleTitle,
new AccessibilityDelegateCompat() {
@Override
public void onInitializeAccessibilityNodeInfo(
View view, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(accessibleTitle, info);
info.setHeading(true);
}
});
}

return titleContainer;
}

/**
* Creates a dialog compatible only with AppCompat activities. This function should be kept in
* sync with {@link createAppDialog}.
*/
private static Dialog createAppCompatDialog(
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
AlertDialog.Builder builder =
new AlertDialog.Builder(activityContext).setTitle(arguments.getString(ARG_TITLE));
AlertDialog.Builder builder = new AlertDialog.Builder(activityContext);

if (arguments.containsKey(ARG_TITLE)) {
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
View accessibleTitle = getAccessibleTitle(activityContext, title);
builder.setCustomTitle(accessibleTitle);
}
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
}
Expand Down Expand Up @@ -104,10 +153,13 @@ private static Dialog createAppCompatDialog(
*/
private static Dialog createAppDialog(
Context activityContext, Bundle arguments, DialogInterface.OnClickListener fragment) {
android.app.AlertDialog.Builder builder =
new android.app.AlertDialog.Builder(activityContext)
.setTitle(arguments.getString(ARG_TITLE));
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(activityContext);

if (arguments.containsKey(ARG_TITLE)) {
String title = Assertions.assertNotNull(arguments.getString(ARG_TITLE));
View accessibleTitle = getAccessibleTitle(activityContext, title);
builder.setCustomTitle(accessibleTitle);
}
if (arguments.containsKey(ARG_BUTTON_POSITIVE)) {
builder.setPositiveButton(arguments.getString(ARG_BUTTON_POSITIVE), fragment);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
android:orientation="horizontal"
android:paddingStart="?android:attr/dialogPreferredPadding"
android:paddingTop="18dp"
android:paddingEnd="?android:attr/dialogPreferredPadding">

<TextView
android:id="@+id/alert_title"
style="?android:attr/windowTitleStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="18sp" />

</LinearLayout>

0 comments on commit 80a3ed7

Please sign in to comment.