Skip to content

Commit

Permalink
Accessibility stage 1 (#1025)
Browse files Browse the repository at this point in the history
## Proposed Changes

MVP for UIAccessibility integration on iOS

## Testing

Test: TODO

## API changes

Added to iOS source set:

```
@ExperimentalComposeApi
sealed class AccessibilitySyncOptions
```

```
@ExperimentalComposeApi
var ComposeUIViewControllerConfiguration.accessibilitySyncOptions: AccessibilitySyncOptions
```

```
@ExperimentalComposeApi
interface AccessibilityDebugLogger
```
  • Loading branch information
elijah-semyonov committed Feb 2, 2024
1 parent d5808c9 commit 85539a8
Show file tree
Hide file tree
Showing 17 changed files with 704 additions and 305 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.mpp.demo.bug.BugReproducers
import androidx.compose.mpp.demo.components.Components
Expand Down Expand Up @@ -150,7 +151,7 @@ class App(
},
navigationIcon = {
Icon(
Icons.Filled.ArrowBack,
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
modifier = Modifier.backButton()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,29 @@ import SwiftUIInteropExample
import UIKitViewOrder
import androidx.compose.runtime.*
import androidx.compose.ui.main.defaultUIKitMain
import androidx.compose.ui.platform.AccessibilityDebugLogger
import androidx.compose.ui.platform.AccessibilitySyncOptions
import androidx.compose.ui.window.ComposeUIViewController
import bugs.IosBugs
import bugs.ProperContainmentDisposal
import bugs.StartRecompositionCheck
import platform.UIKit.UIViewController


@OptIn(ExperimentalComposeApi::class)
fun main(vararg args: String) {
val arg = args.firstOrNull() ?: ""
defaultUIKitMain("ComposeDemo", ComposeUIViewController {
defaultUIKitMain("ComposeDemo", ComposeUIViewController(configure = {
accessibilitySyncOptions = AccessibilitySyncOptions.WhenRequiredByAccessibilityServices(object: AccessibilityDebugLogger {
override fun log(message: Any?) {
if (message == null) {
println()
} else {
println("[a11y]: $message")
}
}
})
}) {
IosDemo(arg)
})
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,7 +29,27 @@ NS_ASSUME_NONNULL_BEGIN
// Redeclared to make it visible to Kotlin for override purposes, workaround for the following issue:
// https://youtrack.jetbrains.com/issue/KT-56001/Kotlin-Native-import-Objective-C-category-members-as-class-members-if-the-category-is-located-in-the-same-file

-(BOOL)accessibilityActivate CMP_MUST_BE_OVERRIDED;
- (NSArray<UIAccessibilityCustomAction *> *)accessibilityCustomActions CMP_MUST_BE_OVERRIDED;

- (UIAccessibilityTraits)accessibilityTraits CMP_MUST_BE_OVERRIDED;

- (NSString *__nullable)accessibilityIdentifier CMP_MUST_BE_OVERRIDED;

- (NSString *__nullable)accessibilityHint CMP_MUST_BE_OVERRIDED;

- (NSString *__nullable)accessibilityLabel CMP_MUST_BE_OVERRIDED;

- (NSString *__nullable)accessibilityValue CMP_MUST_BE_OVERRIDED;

- (CGRect)accessibilityFrame CMP_MUST_BE_OVERRIDED;

- (BOOL)isAccessibilityElement CMP_MUST_BE_OVERRIDED;

- (BOOL)accessibilityActivate CMP_MUST_BE_OVERRIDED;

- (void)accessibilityElementDidBecomeFocused;

- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction CMP_MUST_BE_OVERRIDED;

@end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,7 +43,7 @@ - (void)setAccessibilityContainer:(__nullable id)accessibilityContainer {
// Overrides default accessibilityContainer implementation.
- (__nullable id)accessibilityContainer {
// see https://github.com/flutter/flutter/issues/87247
// TODO: investigate if this bug is still present on supported iOS versions, if it's not, fuse `accessibilityContainer` and `resolveAccessibilityContainer` implementations into a single one (like in `CMPAccessibilityContainer`)
// TODO: investigate if this bug is still present on iOS versions supported by Compose, if it's not, fuse `accessibilityContainer` and `resolveAccessibilityContainer` implementations into a single one (like in `CMPAccessibilityContainer`)
if (_inDealloc) {
return nil;
}
Expand All @@ -60,10 +60,50 @@ + (__nullable id)accessibilityContainerOfObject:(id)object {
return [object accessibilityContainer];
}

- (NSArray<UIAccessibilityCustomAction *> *)accessibilityCustomActions {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (UIAccessibilityTraits)accessibilityTraits {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (NSString *__nullable)accessibilityIdentifier {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (NSString *__nullable)accessibilityHint {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (NSString *__nullable)accessibilityLabel {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (NSString *__nullable)accessibilityValue {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (CGRect)accessibilityFrame {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (BOOL)isAccessibilityElement {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (BOOL)accessibilityActivate {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
CMP_MUST_BE_OVERRIDED_INVARIANT_VIOLATION
}

- (void)accessibilityElementDidBecomeFocused {
[super accessibilityElementDidBecomeFocused];
}

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -114,7 +114,7 @@ - (void)transitLifecycleToStarted {
- (void)scheduleHierarchyContainmentCheck {
double delayInSeconds = 0.5;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
switch (self->_lifecycleState) {
case CMPViewControllerLifecycleStateInitalized:
case CMPViewControllerLifecycleStateDestroyed:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down

0 comments on commit 85539a8

Please sign in to comment.