From f2e402e6e9035dba264cd1219c24ad4d6bcf6bc5 Mon Sep 17 00:00:00 2001 From: Sash Zats Date: Sun, 24 Sep 2023 12:24:19 -0700 Subject: [PATCH] Enable voice over mode on device while running in accessibility exploration mode Summary: All the FoA have different logic depending on whether device is running in VoiceOver mode or not. This results in incorrect accessibility being reported through the Flipper plugin. For example on Instagram we fetch the image descriptions, in Facebook app we group individual elements into the accessibility groups to simplify traversing the screen This tells device that it is in a voice over mode while inspecting accessibility mode is on meaning that it will be the closest approximation to the Reviewed By: nscoding Differential Revision: D49579241 fbshipit-source-id: f3be9057007ee6eefbe3b448f0f97c1fcd2c8f46 --- .../Observer/UIDTreeObserver.m | 9 +++++++ .../Traversal/UIDAllyTraversal.h | 2 ++ .../Traversal/UIDAllyTraversal.m | 26 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m index 168d10b33..555bf238a 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m @@ -33,6 +33,15 @@ [self processNode:node withSnapshot:false withContext:context]; } +- (void)setTraversalMode:(UIDTraversalMode)traversalMode { + if (_traversalMode == traversalMode) { + return; + } + _traversalMode = traversalMode; + [UIDAllyTraversal setVoiceOverServiceEnabled:traversalMode == + UIDTraversalModeAccessibilityHierarchy]; +} + - (void)processNode:(id)node withSnapshot:(BOOL)snapshot withContext:(UIDContext*)context { diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h index 4f97a2e79..879f4cc97 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h @@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN @interface UIDAllyTraversal : NSObject ++ (void)setVoiceOverServiceEnabled:(BOOL)enabled; + @property(nonatomic, class, readonly, getter=isSupported) BOOL supported; - (instancetype)initWithDescriptorRegister: diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m index 7e0ed379d..a92c83895 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m @@ -8,6 +8,7 @@ #if FB_SONARKIT_ENABLED #import "UIDAllyTraversal.h" +#import #import "UIDDescriptorRegister.h" #import "UIDMetadataRegister.h" #import "UIDNode.h" @@ -34,6 +35,12 @@ (_accessibilityLeafDescendantsWithOptions:)]; } ++ (void)setVoiceOverServiceEnabled:(BOOL)enabled { + if (self.isSupported) { + _setVoiceOver(enabled); + } +} + - (instancetype)initWithDescriptorRegister: (UIDDescriptorRegister*)descriptorRegister { self = [super init]; @@ -114,6 +121,25 @@ static BOOL _loadAccessibilityFramework(void) { return isAccessibilityFrameworkLoaded; } +static void _setVoiceOver(BOOL enabled) { + NSString* const accessibilityUtilitiesPath = + [[NSBundle bundleForClass:UIApplication.class] + .bundleURL.URLByDeletingLastPathComponent + .URLByDeletingLastPathComponent + URLByAppendingPathComponent: + @"PrivateFrameworks/AccessibilityUtilities.framework/AccessibilityUtilities"] + .relativePath; + void* handler = dlopen( + [accessibilityUtilitiesPath cStringUsingEncoding:NSUTF8StringEncoding], + RTLD_NOW); + if (!handler) { + return; + } + void (*_AXSVoiceOverTouchSetEnabled)(BOOL) = + dlsym(handler, "_AXSVoiceOverTouchSetEnabled"); + _AXSVoiceOverTouchSetEnabled(enabled); +} + static NSString* _nameForNode(NSObject* node) { NSMutableArray* const parts = [NSMutableArray new]; if (node.accessibilityLabel.length > 0) {