From ecbb115ae3a8cba2977ecab9f52086df860cfb1a Mon Sep 17 00:00:00 2001 From: ash2moon Date: Wed, 19 Mar 2025 16:42:18 -0700 Subject: [PATCH] deprecate Android announcement events and add deprecation warning. (#165195) Due to this [Android deprecation](https://developer.android.com/reference/android/view/View#announceForAccessibility(java.lang.CharSequence)) Flutter needs to show deprecation warnings to developers when using announce on Android. Unfortunately testing this log statement is impossible since robolectric currently does not support API >=36. https://github.com/flutter/flutter/issues/165220 will add the test code in once robolectric supports 36. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../platform/android/io/flutter/Build.java | 17 +++++++++++++++++ .../systemchannels/AccessibilityChannel.java | 12 +++++++++++- .../io/flutter/view/AccessibilityBridge.java | 7 +++++++ .../lib/src/semantics/semantics_event.dart | 9 +++++++++ .../lib/src/semantics/semantics_service.dart | 9 +++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/android/io/flutter/Build.java b/engine/src/flutter/shell/platform/android/io/flutter/Build.java index 09662c6dad..519cbeff55 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/Build.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/Build.java @@ -11,20 +11,37 @@ public class Build { /** For use in place of the Android Build.VERSION_CODES class. */ public static class API_LEVELS { @VisibleForTesting public static final int FLUTTER_MIN = 21; + /** Android 5.0 (Lollipop) */ public static final int API_21 = 21; + /** Android 5.1 (Lollipop MR1) */ public static final int API_22 = 22; + /** Android 6.0 (Marshmallow) */ public static final int API_23 = 23; + /** Android 7.0 (Nougat) */ public static final int API_24 = 24; + /** Android 7.1 (Nougat MR1) */ public static final int API_25 = 25; + /** Android 8.0 (Oreo) */ public static final int API_26 = 26; + /** Android 8.1 (Oreo MR1) */ public static final int API_27 = 27; + /** Android 9 (Pie) */ public static final int API_28 = 28; + /** Android 10 (Q) */ public static final int API_29 = 29; + /** Android 11 (R) */ public static final int API_30 = 30; + /** Android 12 (S) */ public static final int API_31 = 31; + /** Android 12L (Sv2) */ public static final int API_32 = 32; + /** Android 13 (Tiramisu) */ public static final int API_33 = 33; + /** Android 14 (Upside Down Cake) */ public static final int API_34 = 34; + /** Android 15 (Vanilla Ice Cream) */ public static final int API_35 = 35; + /** Android 16 */ + public static final int API_36 = 36; } } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java index f2d12b181a..910997a29e 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java @@ -24,6 +24,7 @@ public class AccessibilityChannel { @NonNull public final FlutterJNI flutterJNI; @Nullable private AccessibilityMessageHandler handler; + @SuppressWarnings("deprecation") public final BasicMessageChannel.MessageHandler parsingMessageHandler = new BasicMessageChannel.MessageHandler() { @Override @@ -181,7 +182,16 @@ public class AccessibilityChannel { * {@link AccessibilityChannel#setAccessibilityMessageHandler(AccessibilityMessageHandler)}. */ public interface AccessibilityMessageHandler extends FlutterJNI.AccessibilityDelegate { - /** The Dart application would like the given {@code message} to be announced. */ + /** + * The Dart application would like the given {@code message} to be announced. + * + *

Using AnnounceSemanticsEvent for accessibility is deprecated on Android. Migrate to using + * semantic properties for a more robust and accessible user experience. + * + * @see announceForAccessibility" + */ + @Deprecated(since = "Android API level 36") void announce(@NonNull String message); /** The user has tapped on the semantics node with the given {@code nodeId}. */ diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 268e9a4e1d..a6cc440ed5 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -303,6 +303,13 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { /** The Dart application would like the given {@code message} to be announced. */ @Override public void announce(@NonNull String message) { + if (Build.VERSION.SDK_INT >= API_LEVELS.API_36) { + Log.w( + TAG, + "Using AnnounceSemanticsEvent for accessibility is deprecated on Android. " + + "Migrate to using semantic properties for a more robust and accessible " + + "user experience. See https://developer.android.com/reference/android/view/View#announceForAccessibility(java.lang.CharSequence)"); + } rootAccessibilityView.announceForAccessibility(message); } diff --git a/packages/flutter/lib/src/semantics/semantics_event.dart b/packages/flutter/lib/src/semantics/semantics_event.dart index ac63dd3187..9b9e034d04 100644 --- a/packages/flutter/lib/src/semantics/semantics_event.dart +++ b/packages/flutter/lib/src/semantics/semantics_event.dart @@ -83,6 +83,15 @@ abstract class SemanticsEvent { /// /// When possible, prefer using mechanisms like [Semantics] to implicitly /// trigger announcements over using this event. +/// +/// ### Android +/// Android has [deprecated announcement events][1] due to its disruptive +/// behavior with TalkBack forcing it to clear its speech queue and speak the +/// provided text. Instead, use mechanisms like [Semantics] to implicitly +/// trigger announcements. +/// +/// [1]: https://developer.android.com/reference/android/view/View#announceForAccessibility(java.lang.CharSequence) +/// class AnnounceSemanticsEvent extends SemanticsEvent { /// Constructs an event that triggers an announcement by the platform. const AnnounceSemanticsEvent( diff --git a/packages/flutter/lib/src/semantics/semantics_service.dart b/packages/flutter/lib/src/semantics/semantics_service.dart index 92dff9bfd7..b78864f0d5 100644 --- a/packages/flutter/lib/src/semantics/semantics_service.dart +++ b/packages/flutter/lib/src/semantics/semantics_service.dart @@ -32,6 +32,15 @@ abstract final class SemanticsService { /// The assertiveness level of the announcement is determined by [assertiveness]. /// Currently, this is only supported by the web engine and has no effect on /// other platforms. The default mode is [Assertiveness.polite]. + /// + /// ### Android + /// Android has [deprecated announcement events][1] due to its disruptive + /// behavior with TalkBack forcing it to clear its speech queue and speak the + /// provided text. Instead, use mechanisms like [Semantics] to implicitly + /// trigger announcements. + /// + /// [1]: https://developer.android.com/reference/android/view/View#announceForAccessibility(java.lang.CharSequence) + /// static Future announce( String message, TextDirection textDirection, {