Skip to content

Commit

Permalink
Make scrollable unfocusable when voiceover is running (flutter#27118)
Browse files Browse the repository at this point in the history
  • Loading branch information
chunhtai committed Jul 1, 2021
1 parent 358a8e8 commit 09343ca
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 8 deletions.
Expand Up @@ -1228,7 +1228,8 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
platformView->SetSemanticsEnabled(true);
platformView->SetAccessibilityFeatures(flags);
#else
bool enabled = UIAccessibilityIsVoiceOverRunning() || UIAccessibilityIsSwitchControlRunning();
_isVoiceOverRunning = UIAccessibilityIsVoiceOverRunning();
bool enabled = _isVoiceOverRunning || UIAccessibilityIsSwitchControlRunning();
if (enabled)
flags |= static_cast<int32_t>(flutter::AccessibilityFeatureFlag::kAccessibleNavigation);
platformView->SetSemanticsEnabled(enabled || UIAccessibilityIsSpeakScreenEnabled());
Expand Down
Expand Up @@ -26,6 +26,7 @@ extern NSNotificationName const FlutterViewControllerShowHomeIndicator;
@interface FlutterViewController ()

@property(nonatomic, readonly) BOOL isPresentingViewController;
@property(nonatomic, readonly) BOOL isVoiceOverRunning;
- (fml::WeakPtr<FlutterViewController>)getWeakPtr;
- (std::shared_ptr<flutter::FlutterPlatformViewsController>&)platformViewsController;
- (FlutterRestorationPlugin*)restorationPlugin;
Expand Down
18 changes: 12 additions & 6 deletions shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
Expand Up @@ -193,12 +193,6 @@ - (void)accessibilityBridgeDidFinishUpdate {
[self setFrame:[_semanticsObject accessibilityFrame]];
[self setContentSize:[self contentSizeInternal]];
[self setContentOffset:[self contentOffsetInternal] animated:NO];
if (self.contentSize.width > self.frame.size.width ||
self.contentSize.height > self.frame.size.height) {
self.isAccessibilityElement = YES;
} else {
self.isAccessibilityElement = NO;
}
}

- (void)setChildren:(NSArray<SemanticsObject*>*)children {
Expand All @@ -219,6 +213,18 @@ - (id)accessibilityContainer {
return _container.get();
}

- (BOOL)isAccessibilityElement {
if (![_semanticsObject isAccessibilityBridgeAlive]) {
return NO;
}
if (self.contentSize.width > self.frame.size.width ||
self.contentSize.height > self.frame.size.height) {
return !_semanticsObject.bridge->isVoiceOverRunning();
} else {
return NO;
}
}

// private methods

- (float)scrollExtentMax {
Expand Down
26 changes: 26 additions & 0 deletions shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm
Expand Up @@ -31,6 +31,7 @@
window_ = [[UIWindow alloc] initWithFrame:kScreenSize];
[window_ addSubview:view_];
}
bool isVoiceOverRunning() const override { return isVoiceOverRunningValue; }
UIView* view() const override { return view_; }
UIView<UITextInput>* textInputView() override { return nil; }
void DispatchSemanticsAction(int32_t id, SemanticsAction action) override {
Expand All @@ -49,6 +50,7 @@ void AccessibilityObjectDidLoseFocus(int32_t id) override {}
return nil;
}
std::vector<SemanticsActionObservation> observations;
bool isVoiceOverRunningValue;

private:
UIView* view_;
Expand Down Expand Up @@ -344,6 +346,30 @@ - (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
XCTAssertEqual([scrollable hitTest:CGPointMake(10, 10) withEvent:nil], nil);
}

- (void)testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning {
flutter::MockAccessibilityBridge* mock = new flutter::MockAccessibilityBridge();
mock->isVoiceOverRunningValue = false;
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();

flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
node.actions = flutter::kHorizontalScrollSemanticsActions;
node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
node.scrollExtentMax = 100.0;
node.scrollPosition = 0.0;

FlutterSemanticsObject* delegate = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
FlutterScrollableSemanticsObject* scrollable =
[[FlutterScrollableSemanticsObject alloc] initWithSemanticsObject:delegate];
SemanticsObject* scrollable_object = static_cast<SemanticsObject*>(scrollable);
[scrollable_object setSemanticsNode:&node];
[scrollable_object accessibilityBridgeDidFinishUpdate];
XCTAssertTrue(scrollable_object.isAccessibilityElement);
mock->isVoiceOverRunningValue = true;
XCTAssertFalse(scrollable_object.isAccessibilityElement);
}

- (void)testSemanticsObjectBuildsAttributedString {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
Expand Down
Expand Up @@ -18,9 +18,9 @@
#include "flutter/lib/ui/semantics/custom_accessibility_action.h"
#include "flutter/lib/ui/semantics/semantics_node.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h"
#include "third_party/skia/include/core/SkRect.h"
Expand Down Expand Up @@ -68,6 +68,8 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {

UIView* view() const override { return view_controller_.view; }

bool isVoiceOverRunning() const override { return view_controller_.isVoiceOverRunning; }

fml::WeakPtr<AccessibilityBridge> GetWeakPtr();

std::shared_ptr<FlutterPlatformViewsController> GetPlatformViewsController() const override {
Expand Down
Expand Up @@ -21,6 +21,7 @@ class AccessibilityBridgeIos {
public:
virtual ~AccessibilityBridgeIos() = default;
virtual UIView* view() const = 0;
virtual bool isVoiceOverRunning() const = 0;
virtual UIView<UITextInput>* textInputView() = 0;
virtual void DispatchSemanticsAction(int32_t id, flutter::SemanticsAction action) = 0;
virtual void DispatchSemanticsAction(int32_t id,
Expand Down
Expand Up @@ -224,6 +224,32 @@ - (void)testUpdateSemanticsOneNode {
OCMVerifyAll(mockFlutterView);
}

- (void)testIsVoiceOverRunning {
flutter::MockDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("AccessibilityBridgeTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*platform_views_controller=*/nil,
/*task_runners=*/runners);
id mockFlutterView = OCMClassMock([FlutterView class]);
id mockFlutterViewController = OCMClassMock([FlutterViewController class]);
OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
OCMStub([mockFlutterViewController isVoiceOverRunning]).andReturn(YES);

__block auto bridge =
std::make_unique<flutter::AccessibilityBridge>(/*view_controller=*/mockFlutterViewController,
/*platform_view=*/platform_view.get(),
/*platform_views_controller=*/nil);

XCTAssertTrue(bridge->isVoiceOverRunning());
}

- (void)testSemanticsDeallocated {
@autoreleasepool {
flutter::MockDelegate mock_delegate;
Expand Down

0 comments on commit 09343ca

Please sign in to comment.